<commit_before>var util = require('util');
    should = require('should'),
    JSUS = require('./../jsus').JSUS;

var a = 0;
var b = 1000;
var v = [];

describe('RANDOM: ', function(){

	describe('#random()', function(){
		before(function(){
			v = [];
			for (var i = a; i < b; i++) {
				v.push(JSUS.random(a,b));
			}
		});

		it('should be within (a,b).', function() {
			var found_b = false;
			var found_a = false;
			for (var i=0; i< v.length; i++) {
				if (v[i] === b) found_b = true;
				if (v[i] === a) found_a = true;
				v[i].should.be.within(a,b);
			}

			found_b.should.be.false;
			found_a.should.be.false;

		});

	});

	describe('#randomInt()', function(){
		before(function(){
			v = [];
			for (var i = a; i < b; i++) {
				v.push(JSUS.randomInt(a,b));
			}
		});

		it('should be within (a,b]. Notice: It can fails when it checks for b', function() {
			var found_b = false;
			var found_a = false;
			for (var i=0; i< v.length; i++) {
				if (v[i] === b) found_b = true;
				if (v[i] === a) found_a = true;
				v[i].should.be.within(a,(b+1));
			}

			found_b.should.be.true;
			found_a.should.be.false;

		});

	});

});

<commit_msg>improved random test<commit_after>var util = require('util');
    should = require('should'),
    JSUS = require('./../jsus').JSUS;

var a = 0;
var b = 10000;
var v = [];

describe('RANDOM: ', function(){

	describe('#random()', function(){
		before(function(){
			v = [];
			for (var i = a; i < b; i++) {
				v.push(JSUS.random(a,b));
			}
		});

		it('should be within (a,b).', function() {
			var found_b = false;
			var found_a = false;
			for (var i=0; i< v.length; i++) {
				if (v[i] === b) found_b = true;
				if (v[i] === a) found_a = true;
				v[i].should.be.within(a,b);
			}

			found_b.should.be.false;
			found_a.should.be.false;

		});

	});

	describe('#randomInt()', function() {
		var found_b = false;
		var found_a = false;

		before(function(){
			v = [];
			for (var i = a; i < b; i++) {
				var n = JSUS.randomInt(a,b);
				if (n === b) found_b = true;
				if (n === a) found_a = true;
				v.push(n);
			}
		});

		it('should be within (a,b].', function() {
			for (var i=0; i< v.length; i++) {
				v[i].should.be.within(a,(b+1));
			}
		});

		it('should be within (a,b]. a should never be found', function() {
			found_a.should.be.false;
		});

		it('should be within (a,b]. b should never be found (can fail)', function() {
			found_b.should.be.true;
		});

	});

});

<|endoftext|>


<commit_before>/* jshint -W097 */// jshint strict:false
/*jslint node: true */
var expect = require('chai').expect;
var setup  = require(__dirname + '/lib/setup');

var objects = null;
var states  = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;

var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);

function checkConnectionOfAdapter(cb, counter) {
    counter = counter || 0;
    console.log('Try check #' + counter);
    if (counter > 30) {
        if (cb) cb('Cannot check connection');
        return;
    }

    states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
        if (err) console.error(err);
        if (state && state.val) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkConnectionOfAdapter(cb, counter + 1);
            }, 1000);
        }
    });
}

function checkValueOfState(id, value, cb, counter) {
    counter = counter || 0;
    if (counter > 20) {
        if (cb) cb('Cannot check value Of State ' + id);
        return;
    }

    states.getState(id, function (err, state) {
        if (err) console.error(err);
        if (value === null && !state) {
            if (cb) cb();
        } else
        if (state && (value === undefined || state.val === value)) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkValueOfState(id, value, cb, counter + 1);
            }, 500);
        }
    });
}

function sendTo(target, command, message, callback) {
    onStateChanged = function (id, state) {
        if (id === 'messagebox.system.adapter.test.0') {
            callback(state.message);
        }
    };

    states.pushMessage('system.adapter.' + target, {
        command:    command,
        message:    message,
        from:       'system.adapter.test.0',
        callback: {
            message: message,
            id:      sendToID++,
            ack:     false,
            time:    (new Date()).getTime()
        }
    });
}

describe('Test ' + adapterShortName + ' adapter', function() {
    before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) {
        this.timeout(600000); // because of first install from npm

        setup.setupController(function () {
            var config = setup.getAdapterConfig();
            // enable adapter
            config.common.enabled  = true;
            config.common.loglevel = 'debug';

            //config.native.dbtype   = 'sqlite';

            setup.setAdapterConfig(config.common, config.native);

            setup.startController(true, function(id, obj) {}, function (id, state) {
                    if (onStateChanged) onStateChanged(id, state);
                },
                function (_objects, _states) {
                    objects = _objects;
                    states  = _states;
                    _done();
                });
        });
    });

/*
    ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY
*/
/*    it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) {
        this.timeout(60000);
        checkConnectionOfAdapter(function (res) {
            if (res) console.log(res);
            expect(res).not.to.be.equal('Cannot check connection');
            objects.setObject('system.adapter.test.0', {
                    common: {

                    },
                    type: 'instance'
                },
                function () {
                    states.subscribeMessage('system.adapter.test.0');
                    done();
                });
        });
    });
*/

/*
    PUT YOUR OWN TESTS HERE USING
    it('Testname', function ( done) {
        ...
    });

    You can also use "sendTo" method to send messages to the started adapter
*/

    it('tvspielfilm: feeds to be parsed', function (done) {
        this.timeout(20000);
        states.getState('tvspielfilm.0.json.heute2200', function (err, fileName) {
            expect(err).to.be.not.ok;
            expect(fileName).to.be.ok;
            expect(fileName.ack).to.be.true;
            states.getState('tvspielfilm.0.json.filme', function (err, fileName) {
                expect(err).to.be.not.ok;
                expect(fileName).to.be.ok;
                expect(fileName.ack).to.be.true;
                states.getState('tvspielfilm.0.json.heute2015', function (err, fileName) {
                    expect(err).to.be.not.ok;
                    expect(fileName).to.be.ok;
                    expect(fileName.ack).to.be.true;
                    states.getState('tvspielfilm.0.json.jetzt', function (err, fileName) {
                        expect(err).to.be.not.ok;
                        expect(fileName).to.be.ok;
                        expect(fileName.ack).to.be.true;
                        done();
                    });
                });
            });
        });
    });

    after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) {
        this.timeout(10000);

        setup.stopController(function (normalTerminated) {
            console.log('Adapter normal terminated: ' + normalTerminated);
            done();
        });
    });
});
<commit_msg>update testing<commit_after>/* jshint -W097 */// jshint strict:false
/*jslint node: true */
var expect = require('chai').expect;
var setup  = require(__dirname + '/lib/setup');

var objects = null;
var states  = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;

var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);

function checkConnectionOfAdapter(cb, counter) {
    counter = counter || 0;
    console.log('Try check #' + counter);
    if (counter > 30) {
        if (cb) cb('Cannot check connection');
        return;
    }

    states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
        if (err) console.error(err);
        if (state && state.val) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkConnectionOfAdapter(cb, counter + 1);
            }, 1000);
        }
    });
}

function checkValueOfState(id, value, cb, counter) {
    counter = counter || 0;
    if (counter > 20) {
        if (cb) cb('Cannot check value Of State ' + id);
        return;
    }

    states.getState(id, function (err, state) {
        if (err) console.error(err);
        if (value === null && !state) {
            if (cb) cb();
        } else
        if (state && (value === undefined || state.val === value)) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkValueOfState(id, value, cb, counter + 1);
            }, 500);
        }
    });
}

function sendTo(target, command, message, callback) {
    onStateChanged = function (id, state) {
        if (id === 'messagebox.system.adapter.test.0') {
            callback(state.message);
        }
    };

    states.pushMessage('system.adapter.' + target, {
        command:    command,
        message:    message,
        from:       'system.adapter.test.0',
        callback: {
            message: message,
            id:      sendToID++,
            ack:     false,
            time:    (new Date()).getTime()
        }
    });
}

describe('Test ' + adapterShortName + ' adapter', function() {
    before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) {
        this.timeout(600000); // because of first install from npm

        setup.setupController(function () {
            var config = setup.getAdapterConfig();
            // enable adapter
            config.common.enabled  = true;
            config.common.loglevel = 'debug';

            //config.native.dbtype   = 'sqlite';

            setup.setAdapterConfig(config.common, config.native);

            setup.startController(true, function(id, obj) {}, function (id, state) {
                    if (onStateChanged) onStateChanged(id, state);
                },
                function (_objects, _states) {
                    objects = _objects;
                    states  = _states;
                    _done();
                });
        });
    });

/*
    ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY
*/
/*    it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) {
        this.timeout(60000);
        checkConnectionOfAdapter(function (res) {
            if (res) console.log(res);
            expect(res).not.to.be.equal('Cannot check connection');
            objects.setObject('system.adapter.test.0', {
                    common: {

                    },
                    type: 'instance'
                },
                function () {
                    states.subscribeMessage('system.adapter.test.0');
                    done();
                });
        });
    });
*/

/*
    PUT YOUR OWN TESTS HERE USING
    it('Testname', function ( done) {
        ...
    });

    You can also use "sendTo" method to send messages to the started adapter
*/

    it('tvspielfilm: feeds to be parsed', function (done) {
        this.timeout(20000);
        setTimeout(function() {
            states.getState('tvspielfilm.0.json.heute2200', function (err, fileName) {
                expect(err).to.be.not.ok;
                expect(fileName).to.be.ok;
                expect(fileName.ack).to.be.true;
                states.getState('tvspielfilm.0.json.filme', function (err, fileName) {
                    expect(err).to.be.not.ok;
                    expect(fileName).to.be.ok;
                    expect(fileName.ack).to.be.true;
                    states.getState('tvspielfilm.0.json.heute2015', function (err, fileName) {
                        expect(err).to.be.not.ok;
                        expect(fileName).to.be.ok;
                        expect(fileName.ack).to.be.true;
                        states.getState('tvspielfilm.0.json.jetzt', function (err, fileName) {
                            expect(err).to.be.not.ok;
                            expect(fileName).to.be.ok;
                            expect(fileName.ack).to.be.true;
                            done();
                        });
                    });
                });
            });
        }, 5000); // give him 5 secs to read the feeds
    });

    after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) {
        this.timeout(10000);

        setup.stopController(function (normalTerminated) {
            console.log('Adapter normal terminated: ' + normalTerminated);
            done();
        });
    });
});
<|endoftext|>


<commit_before>/* jshint -W097 */// jshint strict:false
/*jslint node: true */
var expect = require('chai').expect;
var setup  = require(__dirname + '/lib/setup');

var objects = null;
var states  = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;

var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);

function checkConnectionOfAdapter(cb, counter) {
    counter = counter || 0;
    console.log('Try check #' + counter);
    if (counter > 30) {
        if (cb) cb('Cannot check connection');
        return;
    }

    states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
        if (err) console.error(err);
        if (state && state.val) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkConnectionOfAdapter(cb, counter + 1);
            }, 1000);
        }
    });
}

function checkValueOfState(id, value, cb, counter) {
    counter = counter || 0;
    if (counter > 20) {
        if (cb) cb('Cannot check value Of State ' + id);
        return;
    }

    states.getState(id, function (err, state) {
        if (err) console.error(err);
        if (value === null && !state) {
            if (cb) cb();
        } else
        if (state && (value === undefined || state.val === value)) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkValueOfState(id, value, cb, counter + 1);
            }, 500);
        }
    });
}

function sendTo(target, command, message, callback) {
    onStateChanged = function (id, state) {
        if (id === 'messagebox.system.adapter.test.0') {
            callback(state.message);
        }
    };

    states.pushMessage('system.adapter.' + target, {
        command:    command,
        message:    message,
        from:       'system.adapter.test.0',
        callback: {
            message: message,
            id:      sendToID++,
            ack:     false,
            time:    (new Date()).getTime()
        }
    });
}

describe('Test ' + adapterShortName + ' adapter', function() {
    before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) {
        this.timeout(600000); // because of first install from npm

        setup.setupController(function () {
            var config = setup.getAdapterConfig();
            // enable adapter
            config.common.enabled  = true;
            config.common.loglevel = 'debug';

            //config.native.dbtype   = 'sqlite';

            setup.setAdapterConfig(config.common, config.native);

            setup.startController(true, function(id, obj) {}, function (id, state) {
                    if (onStateChanged) onStateChanged(id, state);
                },
                function (_objects, _states) {
                    objects = _objects;
                    states  = _states;
                    _done();
                });
        });
    });

/*
    ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY
*/
    it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) {
        this.timeout(60000);
        checkConnectionOfAdapter(function (res) {
            if (res) console.log(res);
            expect(res).not.to.be.equal('Cannot check connection');
            objects.setObject('system.adapter.test.0', {
                    common: {

                    },
                    type: 'instance'
                },
                function () {
                    states.subscribeMessage('system.adapter.test.0');
                    done();
                });
        });
    });
/**/

/*
    PUT YOUR OWN TESTS HERE USING
    it('Testname', function ( done) {
        ...
    });

    You can also use "sendTo" method to send messages to the started adapter
*/

    after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) {
        this.timeout(10000);

        setup.stopController(function (normalTerminated) {
            console.log('Adapter normal terminated: ' + normalTerminated);
            done();
        });
    });
});
<commit_msg>device response incl.<commit_after>/* jshint -W097 */// jshint strict:false
/*jslint node: true */
var expect = require('chai').expect;
var setup  = require(__dirname + '/lib/setup');
var http = require('http');

var objects = null;
var states  = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;

var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.')+1);

function checkConnectionOfAdapter(cb, counter) {
    counter = counter || 0;
    console.log('Try check #' + counter);
    if (counter > 30) {
        if (cb) cb('Cannot check connection');
        return;
    }

    states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) {
        if (err) console.error(err);
        if (state && state.val) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkConnectionOfAdapter(cb, counter + 1);
            }, 1000);
        }
    });
}

function checkValueOfState(id, value, cb, counter) {
    counter = counter || 0;
    if (counter > 20) {
        if (cb) cb('Cannot check value Of State ' + id);
        return;
    }

    states.getState(id, function (err, state) {
        if (err) console.error(err);
        if (value === null && !state) {
            if (cb) cb();
        } else
        if (state && (value === undefined || state.val === value)) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkValueOfState(id, value, cb, counter + 1);
            }, 500);
        }
    });
}

function sendTo(target, command, message, callback) {
    onStateChanged = function (id, state) {
        if (id === 'messagebox.system.adapter.test.0') {
            callback(state.message);
        }
    };

    states.pushMessage('system.adapter.' + target, {
        command:    command,
        message:    message,
        from:       'system.adapter.test.0',
        callback: {
            message: message,
            id:      sendToID++,
            ack:     false,
            time:    (new Date()).getTime()
        }
    });
}
//MusicCast Gerät mit http Server Emulieren
var server;

function setupHttpServer(callback) {
    //We need a function which handles requests and send response
    //Create a server
    server = http.createServer(handleHttpRequest);
    //Lets start our server
    server.listen(8080, function() {
        //Callback triggered when server is successfully listening. Hurray!
        console.log("HTTP-Server listening on: http://localhost:%s", 8080);
        callback();
    });
}

//Antworten des MusicCast Gerätes

function handleHttpRequest(request, response) {
    console.log('HTTP-Server: Request: ' + request.method + ' ' + request.url);

    if (request.url == '/YamahaExtendedControl/v1/system/getFeatures') { //check the URL of the current request
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.write(JSON.stringify({"response_code":0,"system":{"func_list":["wired_lan","wireless_lan","wireless_direct","network_standby","auto_power_standby","bluetooth_tx_setting","airplay","speaker_settings","disklavier_settings"],"zone_num":1,"input_list":[{"id":"napster","distribution_enable":true,"rename_enable":false,"account_enable":true,"play_info_type":"netusb"},{"id":"spotify","distribution_enable":true,"rename_enable":false,"account_enable":false,"play_info_type":"netusb"},{"id":"juke","distribution_enable":true,"rename_enable":false,"account_enable":true,"play_info_type":"netusb"},{"id":"tidal","distribution_enable":true,"rename_enable":false,"account_enable":true,"play_info_type":"netusb"},{"id":"deezer","distribution_enable":true,"rename_enable":false,"account_enable":true,"play_info_type":"netusb"},{"id":"airplay","distribution_enable":false,"rename_enable":false,"account_enable":false,"play_info_type":"netusb"},{"id":"mc_link","distribution_enable":false,"rename_enable":true,"account_enable":false,"play_info_type":"netusb"},{"id":"server","distribution_enable":true,"rename_enable":true,"account_enable":false,"play_info_type":"netusb"},{"id":"net_radio","distribution_enable":true,"rename_enable":true,"account_enable":false,"play_info_type":"netusb"},{"id":"bluetooth","distribution_enable":true,"rename_enable":false,"account_enable":false,"play_info_type":"netusb"},{"id":"hdmi","distribution_enable":true,"rename_enable":true,"account_enable":false,"play_info_type":"none"},{"id":"tv","distribution_enable":true,"rename_enable":true,"account_enable":false,"play_info_type":"none"},{"id":"analog","distribution_enable":true,"rename_enable":true,"account_enable":false,"play_info_type":"none"}],"speaker_settings":{"type":"beam","common":{"attribute":2,"range_step":[{"id":"ysp_pos_width","min":300,"max":900,"step":1},{"id":"ysp_pos_length","min":300,"max":900,"step":1},{"id":"ysp_pos_position","min":100,"max":800,"step":1},{"id":"ysp_pos_distance","min":250,"max":850,"step":1}]},"front_l":{"attribute":3,"range_step":[{"id":"level","min":-10,"max":10,"step":1}]},"front_r":{"attribute":3,"range_step":[{"id":"level","min":-10,"max":10,"step":1}]},"center":{"attribute":3,"range_step":[{"id":"level","min":-10,"max":10,"step":1}]},"surr_l":{"attribute":3,"range_step":[{"id":"level","min":-10,"max":10,"step":1}]},"surr_r":{"attribute":3,"range_step":[{"id":"level","min":-10,"max":10,"step":1}]},"swfr":{"attribute":3,"range_step":[{"id":"level","min":-10,"max":10,"step":1}]}},"ymap_list":["vtuner"]},"zone":[{"id":"main","func_list":["power","volume","mute","sound_program","clear_voice","subwoofer_volume","signal_info","prepare_input_change","link_control","link_audio_delay"],"input_list":["napster","spotify","juke","tidal","deezer","airplay","mc_link","server","net_radio","bluetooth","hdmi","tv","analog"],"sound_program_list":["sports","game","music","tv_program","movie","stereo"],"link_control_list":["speed","standard","stability"],"link_audio_delay_list":["audio_sync","lip_sync"],"range_step":[{"id":"volume","min":0,"max":100,"step":1},{"id":"subwoofer_volume","min":-10,"max":10,"step":1}]}],"netusb":{"func_list":["recent_info","play_queue","mc_playlist"],"preset":{"num":40},"recent_info":{"num":40},"play_queue":{"size":200},"mc_playlist":{"size":200,"num":5},"net_radio_type":"vtuner","vtuner_fver":"A","pandora":{"sort_option_list":["date","alphabet"]}},"distribution":{"server_zone_list":["main"]}}));
        response.end();
    }
    else if (request.url == '/YamahaExtendedControl/v1/system/getDeviceInfo') { //check the URL of the current request
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.write(JSON.stringify({"response_code":0,"model_name":"YSP-1600","destination":"BG","device_id":"00A0DED15025","system_id":"0B587073","system_version":2.52,"api_version":1.18,"netmodule_version":"1428    ","netmodule_checksum":"A693CC12","operation_mode":"normal","update_error_code":"00000000"}    ));
        response.end();
    }
    else if (request.url == '/YamahaExtendedControl/v1/netusb/getPlayInfo') { //check the URL of the current request
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.write(JSON.stringify({"response_code":0,"input":"server","play_queue_type":"system","playback":"stop","repeat":"off","shuffle":"off","play_time":0,"total_time":0,"artist":"","album":"","track":"","albumart_url":"","albumart_id":9777,"usb_devicetype":"unknown","auto_stopped":false,"attribute":83902583}));
        response.end();
    }
    else if (request.url == '/YamahaExtendedControl/v1/netusb/getRecentInfo') { //check the URL of the current request
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.write(JSON.stringify({"response_code":0,"recent_info":[{"input":"net_radio","text":"rbb radioeins","albumart_url":"http:\/\/item.radio456.com:80\/007452\/logo\/logo-4716.jpg","play_count":19,"attribute":0},{"input":"net_radio","text":"1LIVE","albumart_url":"http:\/\/item.radio456.com:80\/007452\/logo\/logo-531.jpg","play_count":15,"attribute":0},{"input":"net_radio","text":"BBC Radio 2","albumart_url":"http:\/\/item.radio456.com:80\/007452\/logo\/logo-3159.jpg","play_count":3,"attribute":0},{"input":"net_radio","text":"Antenne Thüringen 97.9 FM","albumart_url":"http:\/\/item.radio456.com:80\/007452\/logo\/logo-6813.jpg","play_count":10,"attribute":0},{"input":"server","text":"   5. you and me","albumart_url":"","play_count":2,"attribute":30},{"input":"server","text":"   7. making a memory","albumart_url":"","play_count":2,"attribute":30},{"input":"server","text":"   6. friends don't let friends dial drunk","albumart_url":"","play_count":1,"attribute":30},{"input":"server","text":"   4. hate (i really don't like you)","albumart_url":"","play_count":1,"attribute":30},{"input":"server","text":"   3. come back to me","albumart_url":"","play_count":2,"attribute":30},{"input":"server","text":"   2. our time now","albumart_url":"","play_count":2,"attribute":30},{"input":"server","text":"   1. hey there delilah","albumart_url":"","play_count":1,"attribute":30},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0},{"input":"unknown","text":"","albumart_url":"","play_count":0,"attribute":0}]}));
        response.end();
    }
    else if (request.url == '/YamahaExtendedControl/v1/netusb/getPresetInfo') { //check the URL of the current request
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.write(JSON.stringify({"response_code":0,"preset_info":[{"input":"net_radio","text":"Antenne Thüringen 97.9 FM","attribute":0},{"input":"net_radio","text":"rbb radioeins","attribute":0},{"input":"net_radio","text":"1LIVE","attribute":0},{"input":"unknown","text":""},{"input":"net_radio","text":"Deutschlandfunk","attribute":0},{"input":"net_radio","text":"BBC Radio 2","attribute":0},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""},{"input":"unknown","text":""}],"func_list":["clear","move"]}));
        response.end();
    }
    else if (request.url == '/YamahaExtendedControl/v1/main/getStatus') { //check the URL of the current request
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.write(JSON.stringify({"response_code":0,"power":"on","volume":67,"mute":false,"max_volume":100,"input":"hdmi","distribution_enable":true,"sound_program":"movie","clear_voice":true,"subwoofer_volume":-10,"link_control":"standard","link_audio_delay":"audio_sync","disable_flags":0}));
        response.end();
    }
    else if (request.url == '/YamahaExtendedControl/v1/zone2/getStatus') { //check the URL of the current request
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.write(JSON.stringify({"response_code" :3 }));
        response.end();
    }
    else {
        response.writeHead(200, { 'Content-Type': 'application/json' });
        response.write(JSON.stringify({"response_code" :3 }));
        response.end();
    }
}

describe('Test ' + adapterShortName + ' adapter', function() {
    before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) {
        this.timeout(600000); // because of first install from npm

        setup.setupController(function () {
            var config = setup.getAdapterConfig();
            // enable adapter
            config.common.enabled  = true;
            config.common.loglevel = 'debug';

            //config.native.dbtype   = 'sqlite';

            setup.setAdapterConfig(config.common, config.native);
            //erstmal nur ein Gerät im Setup
            config.native.ip = '127.0.0.1:8080';
            config.native.type = 'YSP-1600';
            config.native.uid = '00112233';
            config.native.name = 'TestGerät';

            setupHttpServer(function() {
                setup.startController(true, function (id, obj) { }, function (id, state) {
                    if (onStateChanged) onStateChanged(id, state);
                },
                    function (_objects, _states) {
                        objects = _objects;
                        states = _states;
                        _done();
                    });
            });
        });
    });

/*
    ENABLE THIS WHEN ADAPTER RUNS IN DEAMON MODE TO CHECK THAT IT HAS STARTED SUCCESSFULLY
*/
    it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) {
        this.timeout(60000);
        checkConnectionOfAdapter(function (res) {
            if (res) console.log(res);
            expect(res).not.to.be.equal('Cannot check connection');
            objects.setObject('system.adapter.test.0', {
                    common: {

                    },
                    type: 'instance'
                },
                function () {
                    states.subscribeMessage('system.adapter.test.0');
                    done();
                });
        });
    });
/**/

/*
    PUT YOUR OWN TESTS HERE USING
    it('Testname', function ( done) {
        ...
    });

    You can also use "sendTo" method to send messages to the started adapter
*/
    // anfang von eigenen Tests
    it('Test ' + adapterShortName + ' adapter: Check values', function (done) {
        console.log('START CHECK VALUES');
        this.timeout(90000);
        checkValueOfState('musiccast.0.main.volume', 67, function() {
            setTimeout(function() {
                checkValueOfState('musiccast.0.main.volume', 67, function() {
                    done();
                });
            }, 70000);
        });
    });

    /*
    it('Test ' + adapterShortName + ' adapter: Set values', function (done) {
        console.log('START SET VALUES');
        this.timeout(90000);
        states.setState('musiccast.0.main.volume', {val: 20, ack: false, from: 'test.0'}, function (err) {
            if (err) {
                console.log(err);
            }
            checkValueOfState('musiccast.0.main.volume', 20, function() {
                done();
            });
        });
    });
    */

    // schluss mit eigenen Tests
    after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) {
        this.timeout(10000);

        setup.stopController(function (normalTerminated) {
            console.log('Adapter normal terminated: ' + normalTerminated);
            done();
        });
    });
});
<|endoftext|>


<commit_before>var util = require('util');
var PubSub = require('../lib/pubsub_service');
var Logger = require('../lib/logger');
var Runtime = require('../zetta_runtime');
var Scientist = require('zetta-scientist');
var assert = require('assert');
var SensorDriver = require('./fixture/sensor_driver');
var TestDriver = require('./fixture/example_driver');
var MemRegistry = require('./fixture/mem_registry');

describe('Driver', function() {
  var machine = null;
  var pubsub = null;
  var log = null;
  var reg = null;

  beforeEach(function(){
    reg = new MemRegistry();
    pubsub = new PubSub();
    log = new Logger({pubsub: pubsub});
    log.pubsub = pubsub;
    // create machine
    machine = Scientist.create(TestDriver);
    machine._pubsub = pubsub; // setup pubsub, log, registry
    machine._log = log;
    machine._registry = reg;

    // init machine
    machine = Scientist.init(machine);
  });

  it('should be attached to the zetta runtime', function() {
    assert.ok(Runtime.Device);
  });

  describe('Configuration', function() {
    it('should be configured by Scientist#configure', function() {
      assert.ok(machine.call);
      assert.equal(machine.type, 'testdriver');
      assert.equal(machine.state, 'ready');
      assert.equal(machine.name, 'Matt\'s Test Device');
    });

    it('should have an id automatically generated for it', function(){
      assert.ok(machine.id);
    });

    it('should have properties function', function() {
      assert.equal(typeof machine.properties, 'function');
    });

    it('properties function should return filtered property list', function() {
      machine._foo = 123;
      var p = machine.properties();
      assert.equal(p._foo, undefined);
    });

  });

  describe('Logging', function() {
    it('should expose a .log method', function() {
      assert.equal(typeof machine.log, 'function');
    });

    it('should have log level functions', function() {
      assert.ok(machine.log);
      assert.ok(machine.info);
      assert.ok(machine.warn);
      assert.ok(machine.error);
    });
  });

  describe('Transitions', function() {

    it('shuld not throw when calling an invalid transition name.', function(done) {
      machine.call('not-a-transition', function(err) {
        assert(err);
        done();
      });
    });

    it('should not throw when calling a transition not allowed in invalid state.', function(done) {
      machine.state = 'not-a-state';
      machine.call('prepare', function(err) {
        assert(err);
        done();
      });
    });

    it('avialable transitions should not throw when in invalid state.', function(done) {
      machine.state = 'not-a-state';
      machine.transitionsAvailable();
      done();
    });

    it('should change the state from ready to changed when calling change.', function(done) {
      machine.call('change', function() {
        assert.equal(machine.state, 'changed');
        done();
      });
    });

    it('should be able to call transiton afterchange after change was called', function(done) {
      machine.call('change', function() {
        assert.equal(machine.state, 'changed');
        machine.call('prepare', function(err) {
          assert.equal(machine.state, 'ready');
          done();
        });
      });
    });

    it('should not throw an error when a disallowed transition tries to happen.', function(done) {
      assert.doesNotThrow(function(){
        machine.call('change', function() {
          machine.call('change');
          done();
        });
      });
    });

    it('should return error in callback.', function(done) {
      machine.call('error', 'some error', function(err) {
        assert(err instanceof Error);
        done();
      });
    });

    it('should have transitions emitted like events.', function(done) {
      machine.on('change', function() {
        done();
      });

      machine.call('change');
    });

    it('should publish transitions to pubsub', function(done) {
      var topic = machine.type + '/' + machine.id + '/logs';

      var recv = 0;
      pubsub.subscribe(topic, function(topic, msg) {
        assert.ok(msg.timestamp);
        assert.ok(msg.topic);
        assert.ok(!msg.data);
        assert.ok(msg.properties);
        assert.ok(msg.input);
        assert.ok(msg.transition);
        recv++;
      });
      machine.call('change');
      setImmediate(function() {
        assert.equal(recv, 1);
        done();
      });
    });

    it('should publish transitions to logs', function(done) {
      var recv = 0;
      pubsub.subscribe('logs', function(topic, msg) {
        assert.ok(msg.timestamp);
        assert.ok(msg.topic);
        assert.ok(!msg.data);
        assert.ok(msg.properties);
        assert.ok(msg.input);
        assert.ok(msg.transition);
        recv++;
      });
      machine.call('change');
      setImmediate(function() {
        assert.equal(recv, 1);
        done();
      });
    });

    it('transitionsAvailable should return proper transitions', function() {
      //.when('ready', { allow: ['change', 'test'] })
      //.when('changed', { allow: ['prepare', 'test'] })

      machine.state = 'ready';
      var transitions = machine.transitionsAvailable();
      assert(Object.keys(transitions).indexOf('change') > -1);
      assert(Object.keys(transitions).indexOf('test') > -1);

      machine.state = 'changed';
      var transitions = machine.transitionsAvailable();
      assert(Object.keys(transitions).indexOf('prepare') > -1);
      assert(Object.keys(transitions).indexOf('test') > -1);

    });
  });

  describe('Monitors', function(){

    it('should be able to read state property', function() {
      assert.equal(machine.state, 'ready');
    });

    it('should be able to read monitors properties', function() {
      assert.equal(machine.foo, 0);
      machine.foo = 1;
      assert.equal(machine.foo, 1);
    });
  });

  describe('Streams', function(){

    function wireUpPubSub(stream, done){
      pubsub.publish = function(name, data){
        assert.ok(name);
        assert.ok(data);
        assert.ok(name.indexOf(stream) > -1);
        done();
      }
    }

    it('should stream values of foo once configured', function(done){
      assert.ok(machine.streams.foo);
      wireUpPubSub('foo', done);
      machine.foo++;
    });

    it('should have createReadSteam on device', function(){
      assert.ok(machine.createReadStream);
      assert.ok(machine.createReadStream('foo'));
    });

    it('createReadStream should return values from stream', function(done){
      var s = machine.createReadStream('foo');
      s.on('data', function() {
        done();
      });
      machine.foo++;
    });

    it('createReadStream stream when paused shoud not recieve any updates', function(done){
      var s = machine.createReadStream('foo');
      var recv = 0;
      s.on('data', function() {
        recv++;
        if (recv === 1) {
          s.pause();
          machine.foo++;
          setTimeout(done, 10);
        } else {
          throw new Error('Should not recieve more than one data event');
        }
      });
      machine.foo++;
    });

    it('should stream values of bar once configured', function(done){
      assert.ok(machine.streams.bar);
      wireUpPubSub('bar', done);
      machine.incrementStreamValue();
    });

    it('should create a state stream when transitions are present', function() {
      assert.ok(machine.streams.state);
    });

    it('should not create a state stream when no transitions are present', function() {
      var machine = Scientist.init(Scientist.create(SensorDriver));
      assert(!machine.streams.state);
    });
  });

  describe('Device.save', function() {

    it('save should be implemented on device', function() {
      assert.equal(typeof machine.save, 'function');
    });

    it('save should update the registry with new property values', function(cb) {

      reg.get(machine.id, function(err, result) {
        assert(err);

        machine.someval = 123;
        machine._hidden = 'some-string';
        machine.save(function(err) {
          assert(!err);

          reg.get(machine.id, function(err, result) {
            assert.equal(err, null);
            assert.equal(result.id, machine.id);
            assert.equal(result.someval, 123);
            assert.equal(typeof result._hidden, 'undefined');
            cb();
          });
        });
      });
    });

  });

  describe('Remote Update and Fetch Hooks', function() {

    it('can pass config a remoteFetch function to be called when .properties() is called', function() {
      var Device = Runtime.Device;
      var SomeDevice = function() {
        this.hidden = 'hidden prop';
        Device.call(this);
      };
      util.inherits(SomeDevice, Device);
      SomeDevice.prototype.init = function(config) {
        config
          .type('some-device')
          .name('device-1')
          .remoteFetch(function() {
            assert.equal(this.hidden, 'hidden prop');
            return { prop: 123 };
          })
      };

      var machine = Scientist.init(Scientist.create(SomeDevice));
      assert.deepEqual(machine.properties(), {
        name: 'device-1',
        prop: 123,
        type: 'some-device',
        id: machine.id
      });
    })

    it('handle remote update method, will update non reserved properties and remove old properties', function(done) {
      var Device = Runtime.Device;
      var SomeDevice = function() {
        this.ip = '1.2.3.4';
        this.mutable = 'abc';
        this.deleted = 'gone after update';
        Device.call(this);
      };
      util.inherits(SomeDevice, Device);
      SomeDevice.prototype.init = function(config) {
        config
          .type('some-device')
          .name('device-1');
      };

      var machine = Scientist.init(Scientist.create(SomeDevice));
      machine._registry = reg;
      machine._pubsub = pubsub;
      machine._log = log;
      machine._handleRemoteUpdate({ mutable: 123 }, function(err) {
        assert.equal(err, null);
        assert.equal(machine.ip, undefined);
        assert.equal(machine.mutable, 123);
        assert.equal(machine.deleted, undefined);
        done();
      });
    })

    it('can pass config a remoteUpdate function to be called when remoteUpdates are called', function(done) {
      var Device = Runtime.Device;
      var SomeDevice = function() {
        this.ip = '1.2.3.4';
        this.mutable = 'abc';
        this.deleted = 'gone after update';
        Device.call(this);
      };
      util.inherits(SomeDevice, Device);
      SomeDevice.prototype.init = function(config) {
        config
          .type('some-device')
          .name('device-1')
          .remoteUpdate(function(properties, cb) {
            var self = this;
            // make sure ip cant be updated
            delete properties.ip;

            Object.keys(properties).forEach(function(key) {
              self[key] = properties[key];
            });

            this.save(cb);
          })
      };

      var machine = Scientist.init(Scientist.create(SomeDevice));
      machine._registry = reg;
      machine._pubsub = pubsub;
      machine._log = log;
      machine._handleRemoteUpdate({ mutable: 123 }, function(err) {
        assert.equal(err, null);
        assert.equal(machine.ip, '1.2.3.4');
        assert.equal(machine.mutable, 123);
        done();
      });
    })

  })

  describe('Deletion', function() {
    it('should have a destroy function', function() {
      assert.ok(machine.destroy);
    });

    it('should emit a destroy event when destroy is called.', function(done) {
      machine.on('destroy', function(m) {
        assert.ok(m);
        done();
      });
      machine.destroy();
    });

    it('handle remote destroy method, will return true by default', function(done) {
      var Device = Runtime.Device;
      var SomeDevice = function() {
        this.ip = '1.2.3.4';
        this.mutable = 'abc';
        this.deleted = 'gone after update';
        Device.call(this);
      };
      util.inherits(SomeDevice, Device);
      SomeDevice.prototype.init = function(config) {
        config
          .type('some-device')
          .name('device-1');
      };

      var machine = Scientist.init(Scientist.create(SomeDevice));
      machine._registry = reg;
      machine._pubsub = pubsub;
      machine._log = log;
      machine._handleRemoteDestroy(function(err, destroyFlag) {
        assert.equal(err, null);
        assert.equal(destroyFlag, true);
        done();
      });
    });
  });
});
<commit_msg>Added a test for the specific error given to transition callback for when device is in destroyed state.<commit_after>var util = require('util');
var PubSub = require('../lib/pubsub_service');
var Logger = require('../lib/logger');
var Runtime = require('../zetta_runtime');
var Scientist = require('zetta-scientist');
var assert = require('assert');
var SensorDriver = require('./fixture/sensor_driver');
var TestDriver = require('./fixture/example_driver');
var MemRegistry = require('./fixture/mem_registry');

describe('Driver', function() {
  var machine = null;
  var pubsub = null;
  var log = null;
  var reg = null;

  beforeEach(function(){
    reg = new MemRegistry();
    pubsub = new PubSub();
    log = new Logger({pubsub: pubsub});
    log.pubsub = pubsub;
    // create machine
    machine = Scientist.create(TestDriver);
    machine._pubsub = pubsub; // setup pubsub, log, registry
    machine._log = log;
    machine._registry = reg;

    // init machine
    machine = Scientist.init(machine);
  });

  it('should be attached to the zetta runtime', function() {
    assert.ok(Runtime.Device);
  });

  describe('Configuration', function() {
    it('should be configured by Scientist#configure', function() {
      assert.ok(machine.call);
      assert.equal(machine.type, 'testdriver');
      assert.equal(machine.state, 'ready');
      assert.equal(machine.name, 'Matt\'s Test Device');
    });

    it('should have an id automatically generated for it', function(){
      assert.ok(machine.id);
    });

    it('should have properties function', function() {
      assert.equal(typeof machine.properties, 'function');
    });

    it('properties function should return filtered property list', function() {
      machine._foo = 123;
      var p = machine.properties();
      assert.equal(p._foo, undefined);
    });

  });

  describe('Logging', function() {
    it('should expose a .log method', function() {
      assert.equal(typeof machine.log, 'function');
    });

    it('should have log level functions', function() {
      assert.ok(machine.log);
      assert.ok(machine.info);
      assert.ok(machine.warn);
      assert.ok(machine.error);
    });
  });

  describe('Transitions', function() {

    it('should not throw when calling an invalid transition name.', function(done) {
      machine.call('not-a-transition', function(err) {
        assert(err);
        done();
      });
    });

    it('should not throw when calling a transition when destroyed.', function(done) {
      machine.state = 'zetta-device-destroy';
      machine.call('prepare', function(err) {
        assert(err);
        assert.equal(err.message, 'Machine destroyed. Cannot use transition prepare');
        done();
      });
    });

    it('should not throw when calling a transition not allowed in invalid state.', function(done) {
      machine.state = 'not-a-state';
      machine.call('prepare', function(err) {
        assert(err);
        done();
      });
    });

    it('avialable transitions should not throw when in invalid state.', function(done) {
      machine.state = 'not-a-state';
      machine.transitionsAvailable();
      done();
    });

    it('should change the state from ready to changed when calling change.', function(done) {
      machine.call('change', function() {
        assert.equal(machine.state, 'changed');
        done();
      });
    });

    it('should be able to call transiton afterchange after change was called', function(done) {
      machine.call('change', function() {
        assert.equal(machine.state, 'changed');
        machine.call('prepare', function(err) {
          assert.equal(machine.state, 'ready');
          done();
        });
      });
    });

    it('should not throw an error when a disallowed transition tries to happen.', function(done) {
      assert.doesNotThrow(function(){
        machine.call('change', function() {
          machine.call('change');
          done();
        });
      });
    });

    it('should return error in callback.', function(done) {
      machine.call('error', 'some error', function(err) {
        assert(err instanceof Error);
        done();
      });
    });

    it('should have transitions emitted like events.', function(done) {
      machine.on('change', function() {
        done();
      });

      machine.call('change');
    });

    it('should publish transitions to pubsub', function(done) {
      var topic = machine.type + '/' + machine.id + '/logs';

      var recv = 0;
      pubsub.subscribe(topic, function(topic, msg) {
        assert.ok(msg.timestamp);
        assert.ok(msg.topic);
        assert.ok(!msg.data);
        assert.ok(msg.properties);
        assert.ok(msg.input);
        assert.ok(msg.transition);
        recv++;
      });
      machine.call('change');
      setImmediate(function() {
        assert.equal(recv, 1);
        done();
      });
    });

    it('should publish transitions to logs', function(done) {
      var recv = 0;
      pubsub.subscribe('logs', function(topic, msg) {
        assert.ok(msg.timestamp);
        assert.ok(msg.topic);
        assert.ok(!msg.data);
        assert.ok(msg.properties);
        assert.ok(msg.input);
        assert.ok(msg.transition);
        recv++;
      });
      machine.call('change');
      setImmediate(function() {
        assert.equal(recv, 1);
        done();
      });
    });

    it('transitionsAvailable should return proper transitions', function() {
      //.when('ready', { allow: ['change', 'test'] })
      //.when('changed', { allow: ['prepare', 'test'] })

      machine.state = 'ready';
      var transitions = machine.transitionsAvailable();
      assert(Object.keys(transitions).indexOf('change') > -1);
      assert(Object.keys(transitions).indexOf('test') > -1);

      machine.state = 'changed';
      var transitions = machine.transitionsAvailable();
      assert(Object.keys(transitions).indexOf('prepare') > -1);
      assert(Object.keys(transitions).indexOf('test') > -1);

    });
  });

  describe('Monitors', function(){

    it('should be able to read state property', function() {
      assert.equal(machine.state, 'ready');
    });

    it('should be able to read monitors properties', function() {
      assert.equal(machine.foo, 0);
      machine.foo = 1;
      assert.equal(machine.foo, 1);
    });
  });

  describe('Streams', function(){

    function wireUpPubSub(stream, done){
      pubsub.publish = function(name, data){
        assert.ok(name);
        assert.ok(data);
        assert.ok(name.indexOf(stream) > -1);
        done();
      }
    }

    it('should stream values of foo once configured', function(done){
      assert.ok(machine.streams.foo);
      wireUpPubSub('foo', done);
      machine.foo++;
    });

    it('should have createReadSteam on device', function(){
      assert.ok(machine.createReadStream);
      assert.ok(machine.createReadStream('foo'));
    });

    it('createReadStream should return values from stream', function(done){
      var s = machine.createReadStream('foo');
      s.on('data', function() {
        done();
      });
      machine.foo++;
    });

    it('createReadStream stream when paused shoud not recieve any updates', function(done){
      var s = machine.createReadStream('foo');
      var recv = 0;
      s.on('data', function() {
        recv++;
        if (recv === 1) {
          s.pause();
          machine.foo++;
          setTimeout(done, 10);
        } else {
          throw new Error('Should not recieve more than one data event');
        }
      });
      machine.foo++;
    });

    it('should stream values of bar once configured', function(done){
      assert.ok(machine.streams.bar);
      wireUpPubSub('bar', done);
      machine.incrementStreamValue();
    });

    it('should create a state stream when transitions are present', function() {
      assert.ok(machine.streams.state);
    });

    it('should not create a state stream when no transitions are present', function() {
      var machine = Scientist.init(Scientist.create(SensorDriver));
      assert(!machine.streams.state);
    });
  });

  describe('Device.save', function() {

    it('save should be implemented on device', function() {
      assert.equal(typeof machine.save, 'function');
    });

    it('save should update the registry with new property values', function(cb) {

      reg.get(machine.id, function(err, result) {
        assert(err);

        machine.someval = 123;
        machine._hidden = 'some-string';
        machine.save(function(err) {
          assert(!err);

          reg.get(machine.id, function(err, result) {
            assert.equal(err, null);
            assert.equal(result.id, machine.id);
            assert.equal(result.someval, 123);
            assert.equal(typeof result._hidden, 'undefined');
            cb();
          });
        });
      });
    });

  });

  describe('Remote Update and Fetch Hooks', function() {

    it('can pass config a remoteFetch function to be called when .properties() is called', function() {
      var Device = Runtime.Device;
      var SomeDevice = function() {
        this.hidden = 'hidden prop';
        Device.call(this);
      };
      util.inherits(SomeDevice, Device);
      SomeDevice.prototype.init = function(config) {
        config
          .type('some-device')
          .name('device-1')
          .remoteFetch(function() {
            assert.equal(this.hidden, 'hidden prop');
            return { prop: 123 };
          })
      };

      var machine = Scientist.init(Scientist.create(SomeDevice));
      assert.deepEqual(machine.properties(), {
        name: 'device-1',
        prop: 123,
        type: 'some-device',
        id: machine.id
      });
    })

    it('handle remote update method, will update non reserved properties and remove old properties', function(done) {
      var Device = Runtime.Device;
      var SomeDevice = function() {
        this.ip = '1.2.3.4';
        this.mutable = 'abc';
        this.deleted = 'gone after update';
        Device.call(this);
      };
      util.inherits(SomeDevice, Device);
      SomeDevice.prototype.init = function(config) {
        config
          .type('some-device')
          .name('device-1');
      };

      var machine = Scientist.init(Scientist.create(SomeDevice));
      machine._registry = reg;
      machine._pubsub = pubsub;
      machine._log = log;
      machine._handleRemoteUpdate({ mutable: 123 }, function(err) {
        assert.equal(err, null);
        assert.equal(machine.ip, undefined);
        assert.equal(machine.mutable, 123);
        assert.equal(machine.deleted, undefined);
        done();
      });
    })

    it('can pass config a remoteUpdate function to be called when remoteUpdates are called', function(done) {
      var Device = Runtime.Device;
      var SomeDevice = function() {
        this.ip = '1.2.3.4';
        this.mutable = 'abc';
        this.deleted = 'gone after update';
        Device.call(this);
      };
      util.inherits(SomeDevice, Device);
      SomeDevice.prototype.init = function(config) {
        config
          .type('some-device')
          .name('device-1')
          .remoteUpdate(function(properties, cb) {
            var self = this;
            // make sure ip cant be updated
            delete properties.ip;

            Object.keys(properties).forEach(function(key) {
              self[key] = properties[key];
            });

            this.save(cb);
          })
      };

      var machine = Scientist.init(Scientist.create(SomeDevice));
      machine._registry = reg;
      machine._pubsub = pubsub;
      machine._log = log;
      machine._handleRemoteUpdate({ mutable: 123 }, function(err) {
        assert.equal(err, null);
        assert.equal(machine.ip, '1.2.3.4');
        assert.equal(machine.mutable, 123);
        done();
      });
    })

  })

  describe('Deletion', function() {
    it('should have a destroy function', function() {
      assert.ok(machine.destroy);
    });

    it('should emit a destroy event when destroy is called.', function(done) {
      machine.on('destroy', function(m) {
        assert.ok(m);
        done();
      });
      machine.destroy();
    });

    it('handle remote destroy method, will return true by default', function(done) {
      var Device = Runtime.Device;
      var SomeDevice = function() {
        this.ip = '1.2.3.4';
        this.mutable = 'abc';
        this.deleted = 'gone after update';
        Device.call(this);
      };
      util.inherits(SomeDevice, Device);
      SomeDevice.prototype.init = function(config) {
        config
          .type('some-device')
          .name('device-1');
      };

      var machine = Scientist.init(Scientist.create(SomeDevice));
      machine._registry = reg;
      machine._pubsub = pubsub;
      machine._log = log;
      machine._handleRemoteDestroy(function(err, destroyFlag) {
        assert.equal(err, null);
        assert.equal(destroyFlag, true);
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>/* eslint no-console:0 */

// mocha/jsdom setup from:
// http://reactjsnews.com/testing-in-react/

import jsdom from 'jsdom';
import chai from 'chai';
import chaiEnzyme from 'chai-enzyme';

// https://github.com/producthunt/chai-enzyme
chai.use(chaiEnzyme());

// setup the simplest document possible
const doc = jsdom.jsdom('<!doctype html>');

// get the window object out of the document
const win = doc.defaultView;

// set globals for mocha that make access to document and window feel
// natural in the test environment
global.document = doc;
global.window = win;

// from mocha-jsdom https://github.com/rstacruz/mocha-jsdom/blob/master/index.js#L80
function propagateToGlobal(window) {
  for (const key of Object.keys(window)) {
    if (key in global) {
      // eslint-disable-next-line no-continue
      continue;
    }

    global[key] = window[key];
  }
}

// take all properties of the window object and also attach it to the
// mocha global object
propagateToGlobal(win);
<commit_msg>Replace syntax disallowed by eslint config<commit_after>/* eslint no-console:0 */

// mocha/jsdom setup from:
// http://reactjsnews.com/testing-in-react/

import jsdom from 'jsdom';
import chai from 'chai';
import chaiEnzyme from 'chai-enzyme';

// https://github.com/producthunt/chai-enzyme
chai.use(chaiEnzyme());

// setup the simplest document possible
const doc = jsdom.jsdom('<!doctype html>');

// get the window object out of the document
const win = doc.defaultView;

// set globals for mocha that make access to document and window feel
// natural in the test environment
global.document = doc;
global.window = win;

// from mocha-jsdom https://github.com/rstacruz/mocha-jsdom/blob/master/index.js#L80
function propagateToGlobal(window) {
  Object.keys(window).forEach((key) => {
    if (key in global) {
      return;
    }

    global[key] = window[key];
  });
}

// take all properties of the window object and also attach it to the
// mocha global object
propagateToGlobal(win);
<|endoftext|>


<commit_before>'use strict';

const { MovieInfo } = require('../../models/types.js');
const leech = require('../leech-promise.js');
const util = require('../util.js');
const dict = require('../category-dictionary.js');

const NAME = 'fc2';
module.exports.name = function () {
    return NAME;
}

const TEMPLATE = {
    "search": "",
    "id": "http://adult.contents.fc2.com/article_search.php?id={qtext}",
}

const DOMAIN = 'adult.contents.fc2.com';
module.exports.domain = function () {
    return DOMAIN;
}

const BASE_URL = 'http://' + DOMAIN;

function crawl (opt) {
    let url = "";
    if (typeof url == "string") {
        url = opt;
    }

    let qtext = opt.qtext || "";
    if (qtext) {
        url = TEMPLATE["id"].replace("{qtext}", qtext);
    }

    if (url == "") {
        throw new Error("Invalid Arguments");
    }

    return leech.get(url)
    .then($ => {
        let info = new MovieInfo({ url: url, country: 'Japan', origlang: 'Japanese' });

        info.origtitle = $('h2.title_bar').text().trim();

        let dReleaseDate = $('.main_info_block dt:contains("販売日")').next().text().trim();
        info.releasedate = util.replaceAll(dReleaseDate, '/', '-');
        info.year = dReleaseDate.slice(0, 4);

        let dDirector = $('.main_info_block dt:contains("販売者")').next().find('a');
        // info.director = {
        //     url: BASE_URL + dDirector.attr("href"),
        //     text: dDirector.text()
        // }

        info.posters.push({
            url: $('a.analyticsLinkClick_mainThum').attr('href')
        });

        info.thumb.push({
            url: $('a.analyticsLinkClick_mainThum').attr('href')
        });

        $('ul.images a').each((i, el) => {
            let ele = $(el);
            info.screenshots.push({
                url: ele.attr('href')
            })
        });

        info.maker = info.director.text;

        info.description = $('section.explain p.text').html().trim();

        info.genres.push({
            url: '',
            text: 'Uncensored'
        });

        info.label = {
            url: info.director.url,
            text: info.director.url.split('/').slice(-2, -1)[0]
        };

        return info;
    });
}

module.exports.crawl = crawl;
<commit_msg>update crawler<commit_after>'use strict';

const { MovieInfo } = require('../../models/types.js');
const leech = require('../leech-promise.js');
const util = require('../util.js');
const dict = require('../category-dictionary.js');

const NAME = 'fc2';
module.exports.name = function () {
    return NAME;
}

const TEMPLATE = {
    "search": "",
    "id": "http://adult.contents.fc2.com/article_search.php?id={qtext}",
}

const DOMAIN = 'adult.contents.fc2.com';
module.exports.domain = function () {
    return DOMAIN;
}

const BASE_URL = 'http://' + DOMAIN;

function crawl (opt) {
    let url = "";
    if (typeof url == "string") {
        url = opt;
    }

    let qtext = opt.qtext || "";
    if (qtext) {
        url = TEMPLATE["id"].replace("{qtext}", qtext);
    }

    if (url == "") {
        throw new Error("Invalid Arguments");
    }

    return leech.get(url)
    .then($ => {
        let info = new MovieInfo({ url: url, country: 'Japan', origlang: 'Japanese' });

        info.origtitle = $('h2.title_bar').text().trim();

        let dReleaseDate = $('.main_info_block dt:contains("販売日")').next().text().trim();
        info.releasedate = util.replaceAll(dReleaseDate, '/', '-');
        info.year = dReleaseDate.slice(0, 4);

        info.posters.push({
            url: $('a.analyticsLinkClick_mainThum').attr('href')
        });

        info.thumb.push({
            url: $('a.analyticsLinkClick_mainThum').attr('href')
        });

        $('ul.images a').each((i, el) => {
            let ele = $(el);
            info.screenshots.push({
                url: ele.attr('href')
            })
        });

        let dDirector = $('.main_info_block dt:contains("販売者")').next().find('a');
        let makerURL = BASE_URL + dDirector.attr("href");

        // info.director = {
        //     url: BASE_URL + dDirector.attr("href"),
        //     text: dDirector.text()
        // }

        info.label = {
            url: makerURL,
            text: makerURL.split('/').slice(-2, -1)[0]
        };

        info.maker = dDirector.text();

        info.description = $('section.explain p.text').html().trim();

        info.genres.push({
            url: '',
            text: 'Uncensored'
        });

        return info;
    });
}

module.exports.crawl = crawl;
<|endoftext|>


<commit_before>define("dojox/data/ClientFilter", ["dojo", "dojox", "dojo/data/util/filter"], function(dojo, dojox) {

// This is an abstract data store module for adding updateable result set functionality to an existing data store class
(function(){
	var cf;
	var addUpdate = function(store,create,remove){
		// create a handler that adds to the list of notifications
		return function(item){
			store._updates.push({
					create:create && item,
					remove:remove && item
				});
			cf.onUpdate();
		}
	};
	cf = dojo.declare("dojox.data.ClientFilter",
		null,
		{
			cacheByDefault: false,
			constructor: function(){
				// summary:
				//		This is an abstract class that data stores can extend to add updateable result set functionality
				// 		as well as client side querying capabilities. This enables
				//		widgets to be aware of how active results change in response to the modifications/notifications.
				//
				//	description:
				//		To a update a result set after a notification (onNew, onSet, and onDelete),
				// 		widgets can call the updateResultSet method. Widgets can use the updated
				//		result sets to determine how to react to notifications, and how to update their displayed results
				//		based on changes.
				//
				// 		This module will use the best available information to update result sets, using query attribute
				// 		objects to determine if items are in a result set, and using the sort arrays to maintain sort
				// 		information. However, queries can be opaque strings, and this module can not update
				// 		results by itself in this case. In this situations, data stores can provide a isUpdateable(request) function
				// 		and matchesQuery(item,request) function. If a data store can handle a query, it can return true from
				// 		isUpdateable and if an item matches a query, it can return true from matchesQuery. Here is
				//		definition of isUpdateable and matchesQuery
				// 		isUpdateable(request)  - request is the keywords arguments as is passed to the fetch function.
				// 		matchesQuery(item,request) - item is the item to test, and request is the value arguments object
				//				for the fetch function.
				//
				//		You can define a property on this object instance "cacheByDefault" to a value of true that will
				// 		cause all queries to be cached by default unless the cache queryOption is explicitly set to false.
				// 		This can be defined in the constructor options for ServiceStore/JsonRestStore and subtypes.
				//
				// example:
				//		to make a updated-result-set data store from an existing data store:
				//	|	dojo.declare("dojox.data.MyLiveDataStore",
				//	|		dojox.data.MyDataStore,dojox.data.ClientFilter], // subclass LiveResultSets if available
				//	|		{}
				//	|	);
				this.onSet = addUpdate(this,true,true);
				this.onNew = addUpdate(this,true,false);
				this.onDelete = addUpdate(this,false,true);
				this._updates= [];
				this._fetchCache = [];
			},
			clearCache: function(){
				//	summary:
				//		Clears the cache of client side queries
				this._fetchCache = [];
			},
			updateResultSet: function(/*Array*/ resultSet, /*Object*/ request){
				//	summary:
				//		Attempts to update the given result set based on previous notifications
				//	resultSet:
				//		The result set array that should be updated
				//	request:
				//		This object follows the same meaning as the keywordArgs passed to a dojo.data.api.Read.fetch.
				//	description:
				// 		This will attempt to update the provide result based on previous notification, adding new items
				// 		from onNew calls, removing deleted items, and updating modified items, and properly removing
				//  	and adding items as required by the query and sort parameters. This function will return:
				//		0: Indicates it could not successfully update the result set
				//		1: Indicates it could successfully handle all the notifications, but no changes were made to the result set
				//		2: Indicates it successfully handled all the notifications and result set has been updated.
				if(this.isUpdateable(request)){
					// we try to avoid rerunning notification updates more than once on the same object for performance
					for(var i = request._version || 0; i < this._updates.length;i++){
						// for each notification,we will update the result set
						var create = this._updates[i].create;
						var remove = this._updates[i].remove;
						if(remove){
							for(var j = 0; j < resultSet.length;j++){
								if(this.getIdentity(resultSet[j]) == this.getIdentity(remove)){
									resultSet.splice(j--,1);
									var updated = true;
								}
							}
						}
						if(create && this.matchesQuery(create,request) && // if there is a new/replacement item and it matches the query
								dojo.indexOf(resultSet,create) == -1){ // and it doesn't already exist in query
							resultSet.push(create); // should this go at the beginning by default instead?
							updated = true;
						}
					}
					if(request.sort && updated){
						// do the sort if needed
						resultSet.sort(this.makeComparator(request.sort.concat()));
					}
					resultSet._fullLength = resultSet.length;
					if(request.count && updated && request.count !== Infinity){
						// do we really need to do this?
						// make sure we still find within the defined paging set
						resultSet.splice(request.count, resultSet.length);
					}
					request._version = this._updates.length;
					return updated ? 2 : 1;
				}
				return 0;
			},
			querySuperSet: function(argsSuper, argsSub){
				//	summary:
				//		Determines whether the provided arguments are super/sub sets of each other
				// argsSuper:
				// 		Dojo Data Fetch arguments
				// argsSub:
				// 		Dojo Data Fetch arguments
				if(argsSuper.query == argsSub.query){
					return {};
				}
				if(!(argsSub.query instanceof Object && // sub query must be an object
						// super query must be non-existent or an object
						(!argsSuper.query || typeof argsSuper.query == 'object'))){
					return false;
				}
				var clientQuery = dojo.mixin({},argsSub.query);
				for(var i in argsSuper.query){
					if(clientQuery[i] == argsSuper.query[i]){
						delete clientQuery[i];
					}else if(!(typeof argsSuper.query[i] == 'string' &&
							// if it is a pattern, we can test to see if it is a sub-pattern
							// FIXME: This is not technically correct, but it will work for the majority of cases
							dojo.data.util.filter.patternToRegExp(argsSuper.query[i]).test(clientQuery[i]))){
						return false;
					}
				}
				return clientQuery;
			},
			//	This is the point in the version notification history at which it is known that the server is in sync, this should
			//	be updated to this._updates.length on commit operations.
			serverVersion: 0,

			cachingFetch: function(args){
				var self = this;
				for(var i = 0; i < this._fetchCache.length;i++){
					var cachedArgs = this._fetchCache[i];
					var clientQuery = this.querySuperSet(cachedArgs,args);
					if(clientQuery !== false){
						var defResult = cachedArgs._loading;
						if(!defResult){
							defResult = new dojo.Deferred();
							defResult.callback(cachedArgs.cacheResults);
						}
						defResult.addCallback(function(results){
							results = self.clientSideFetch(dojo.mixin(dojo.mixin({}, args),{query:clientQuery}), results);
							defResult.fullLength = results._fullLength;
							return results;
						});
						args._version = cachedArgs._version;
						break;
					}
				}
				if(!defResult){
					var serverArgs = dojo.mixin({}, args);
					var putInCache = (args.queryOptions || 0).cache;
					var fetchCache = this._fetchCache;
					if(putInCache === undefined ? this.cacheByDefault : putInCache){
						// we are caching this request, so we want to get all the data, and page on the client side
						if(args.start || args.count){
							delete serverArgs.start;
							delete serverArgs.count;
							args.clientQuery = dojo.mixin(args.clientQuery || {}, {
								start: args.start,
								count: args.count
							});
						}
						args = serverArgs;
						fetchCache.push(args);
					}
					defResult= args._loading = this._doQuery(args);

					defResult.addErrback(function(){
						fetchCache.splice(dojo.indexOf(fetchCache, args), 1);
					});
				}
				var version = this.serverVersion;

				defResult.addCallback(function(results){
					delete args._loading;
					// update the result set in case anything changed while we were waiting for the fetch
					if(results){
						args._version = typeof args._version == "number" ? args._version : version;
						self.updateResultSet(results,args);
						args.cacheResults = results;
						if(!args.count || results.length < args.count){
							defResult.fullLength = ((args.start)?args.start:0) + results.length;
						}
					}
					return results;
				});
				return defResult;
			},
			isUpdateable: function(/*Object*/ request){
				//	summary:
				//		Returns whether the provide fetch arguments can be used to update an existing list
				//	request:
				//		See dojo.data.api.Read.fetch request

				return typeof request.query == "object";
			},
			clientSideFetch: function(/*Object*/ request,/*Array*/ baseResults){
				// summary:
				//		Performs a query on the client side and returns the results as an array
				//
				//	request:
				//		See dojo.data.api.Read.fetch request
				//
				//	baseResults:
				//		This provides the result set to start with for client side querying
				if(request.queryOptions && request.queryOptions.results){
					baseResults = request.queryOptions.results;
				}
				if(request.query){
					// filter by the query
					var results = [];
					for(var i = 0; i < baseResults.length; i++){
						var value = baseResults[i];
						if(value && this.matchesQuery(value,request)){
							results.push(baseResults[i]);
						}
					}
				}else{
					results = request.sort ? baseResults.concat() : baseResults; // we don't want to mutate the baseResults if we are doing a sort
				}
				if(request.sort){
					// do the sort if needed
					results.sort(this.makeComparator(request.sort.concat()));
				}
				return this.clientSidePaging(request, results);
			},
			clientSidePaging: function(/*Object*/ request,/*Array*/ baseResults){
				var start = request.start || 0;
				var finalResults = (start || request.count) ? baseResults.slice(start,start + (request.count || baseResults.length)) : baseResults;
				finalResults._fullLength = baseResults.length;
				return finalResults;
			},
			matchesQuery: function(item,request){
				var query = request.query;
				var ignoreCase = request.queryOptions && request.queryOptions.ignoreCase;
				for(var i in query){
					// if anything doesn't match, than this should be in the query
					var match = query[i];
					var value = this.getValue(item,i);
					if((typeof match == 'string' && (match.match(/[\*\.]/) || ignoreCase)) ?
						!dojo.data.util.filter.patternToRegExp(match, ignoreCase).test(value) :
						value != match){
						return false;
					}
				}
				return true;
			},
			makeComparator: function(sort){
				//	summary:
				// 		returns a comparator function for the given sort order array
				//	sort:
				//		See dojox.data.api.Read.fetch
				var current = sort.shift();
				if(!current){
					// sort order for ties and no sort orders
					return function(){
						return 0;// keep the order unchanged
					};
				}
				var attribute = current.attribute;
				var descending = !!current.descending;
				var next = this.makeComparator(sort);
				var store = this;
				return function(a,b){
					var av = store.getValue(a,attribute);
					var bv = store.getValue(b,attribute);
					if(av != bv){
						return av < bv == descending ? 1 : -1;
					}
					return next(a,b);
				};
			}
		}
	);
	cf.onUpdate = function(){};
})();

return dojox.data.ClientFilter;
});
<commit_msg>refs #13101 - removing unnecessary iife<commit_after>define("dojox/data/ClientFilter", ["dojo", "dojox", "dojo/data/util/filter"], function(dojo, dojox) {

// This is an abstract data store module for adding updateable result set functionality to an existing data store class
	var cf;
	var addUpdate = function(store,create,remove){
		// create a handler that adds to the list of notifications
		return function(item){
			store._updates.push({
					create:create && item,
					remove:remove && item
				});
			cf.onUpdate();
		}
	};
	cf = dojo.declare("dojox.data.ClientFilter",
		null,
		{
			cacheByDefault: false,
			constructor: function(){
				// summary:
				//		This is an abstract class that data stores can extend to add updateable result set functionality
				//		as well as client side querying capabilities. This enables
				//		widgets to be aware of how active results change in response to the modifications/notifications.
				//
				//	description:
				//		To a update a result set after a notification (onNew, onSet, and onDelete),
				//		widgets can call the updateResultSet method. Widgets can use the updated
				//		result sets to determine how to react to notifications, and how to update their displayed results
				//		based on changes.
				//
				//		This module will use the best available information to update result sets, using query attribute
				//		objects to determine if items are in a result set, and using the sort arrays to maintain sort
				//		information. However, queries can be opaque strings, and this module can not update
				//		results by itself in this case. In this situations, data stores can provide a isUpdateable(request) function
				//		and matchesQuery(item,request) function. If a data store can handle a query, it can return true from
				//		isUpdateable and if an item matches a query, it can return true from matchesQuery. Here is
				//		definition of isUpdateable and matchesQuery
				//		isUpdateable(request)  - request is the keywords arguments as is passed to the fetch function.
				//		matchesQuery(item,request) - item is the item to test, and request is the value arguments object
				//				for the fetch function.
				//
				//		You can define a property on this object instance "cacheByDefault" to a value of true that will
				//		cause all queries to be cached by default unless the cache queryOption is explicitly set to false.
				//		This can be defined in the constructor options for ServiceStore/JsonRestStore and subtypes.
				//
				// example:
				//		to make a updated-result-set data store from an existing data store:
				//	|	dojo.declare("dojox.data.MyLiveDataStore",
				//	|		dojox.data.MyDataStore,dojox.data.ClientFilter], // subclass LiveResultSets if available
				//	|		{}
				//	|	);
				this.onSet = addUpdate(this,true,true);
				this.onNew = addUpdate(this,true,false);
				this.onDelete = addUpdate(this,false,true);
				this._updates= [];
				this._fetchCache = [];
			},
			clearCache: function(){
				//	summary:
				//		Clears the cache of client side queries
				this._fetchCache = [];
			},
			updateResultSet: function(/*Array*/ resultSet, /*Object*/ request){
				//	summary:
				//		Attempts to update the given result set based on previous notifications
				//	resultSet:
				//		The result set array that should be updated
				//	request:
				//		This object follows the same meaning as the keywordArgs passed to a dojo.data.api.Read.fetch.
				//	description:
				// 		This will attempt to update the provide result based on previous notification, adding new items
				// 		from onNew calls, removing deleted items, and updating modified items, and properly removing
				//  	and adding items as required by the query and sort parameters. This function will return:
				//		0: Indicates it could not successfully update the result set
				//		1: Indicates it could successfully handle all the notifications, but no changes were made to the result set
				//		2: Indicates it successfully handled all the notifications and result set has been updated.
				if(this.isUpdateable(request)){
					// we try to avoid rerunning notification updates more than once on the same object for performance
					for(var i = request._version || 0; i < this._updates.length;i++){
						// for each notification,we will update the result set
						var create = this._updates[i].create;
						var remove = this._updates[i].remove;
						if(remove){
							for(var j = 0; j < resultSet.length;j++){
								if(this.getIdentity(resultSet[j]) == this.getIdentity(remove)){
									resultSet.splice(j--,1);
									var updated = true;
								}
							}
						}
						if(create && this.matchesQuery(create,request) && // if there is a new/replacement item and it matches the query
								dojo.indexOf(resultSet,create) == -1){ // and it doesn't already exist in query
							resultSet.push(create); // should this go at the beginning by default instead?
							updated = true;
						}
					}
					if(request.sort && updated){
						// do the sort if needed
						resultSet.sort(this.makeComparator(request.sort.concat()));
					}
					resultSet._fullLength = resultSet.length;
					if(request.count && updated && request.count !== Infinity){
						// do we really need to do this?
						// make sure we still find within the defined paging set
						resultSet.splice(request.count, resultSet.length);
					}
					request._version = this._updates.length;
					return updated ? 2 : 1;
				}
				return 0;
			},
			querySuperSet: function(argsSuper, argsSub){
				//	summary:
				//		Determines whether the provided arguments are super/sub sets of each other
				// argsSuper:
				//		Dojo Data Fetch arguments
				// argsSub:
				//		Dojo Data Fetch arguments
				if(argsSuper.query == argsSub.query){
					return {};
				}
				if(!(argsSub.query instanceof Object && // sub query must be an object
						// super query must be non-existent or an object
						(!argsSuper.query || typeof argsSuper.query == 'object'))){
					return false;
				}
				var clientQuery = dojo.mixin({},argsSub.query);
				for(var i in argsSuper.query){
					if(clientQuery[i] == argsSuper.query[i]){
						delete clientQuery[i];
					}else if(!(typeof argsSuper.query[i] == 'string' &&
							// if it is a pattern, we can test to see if it is a sub-pattern
							// FIXME: This is not technically correct, but it will work for the majority of cases
							dojo.data.util.filter.patternToRegExp(argsSuper.query[i]).test(clientQuery[i]))){
						return false;
					}
				}
				return clientQuery;
			},
			//	This is the point in the version notification history at which it is known that the server is in sync, this should
			//	be updated to this._updates.length on commit operations.
			serverVersion: 0,

			cachingFetch: function(args){
				var self = this;
				for(var i = 0; i < this._fetchCache.length;i++){
					var cachedArgs = this._fetchCache[i];
					var clientQuery = this.querySuperSet(cachedArgs,args);
					if(clientQuery !== false){
						var defResult = cachedArgs._loading;
						if(!defResult){
							defResult = new dojo.Deferred();
							defResult.callback(cachedArgs.cacheResults);
						}
						defResult.addCallback(function(results){
							results = self.clientSideFetch(dojo.mixin(dojo.mixin({}, args),{query:clientQuery}), results);
							defResult.fullLength = results._fullLength;
							return results;
						});
						args._version = cachedArgs._version;
						break;
					}
				}
				if(!defResult){
					var serverArgs = dojo.mixin({}, args);
					var putInCache = (args.queryOptions || 0).cache;
					var fetchCache = this._fetchCache;
					if(putInCache === undefined ? this.cacheByDefault : putInCache){
						// we are caching this request, so we want to get all the data, and page on the client side
						if(args.start || args.count){
							delete serverArgs.start;
							delete serverArgs.count;
							args.clientQuery = dojo.mixin(args.clientQuery || {}, {
								start: args.start,
								count: args.count
							});
						}
						args = serverArgs;
						fetchCache.push(args);
					}
					defResult= args._loading = this._doQuery(args);

					defResult.addErrback(function(){
						fetchCache.splice(dojo.indexOf(fetchCache, args), 1);
					});
				}
				var version = this.serverVersion;

				defResult.addCallback(function(results){
					delete args._loading;
					// update the result set in case anything changed while we were waiting for the fetch
					if(results){
						args._version = typeof args._version == "number" ? args._version : version;
						self.updateResultSet(results,args);
						args.cacheResults = results;
						if(!args.count || results.length < args.count){
							defResult.fullLength = ((args.start)?args.start:0) + results.length;
						}
					}
					return results;
				});
				return defResult;
			},
			isUpdateable: function(/*Object*/ request){
				//	summary:
				//		Returns whether the provide fetch arguments can be used to update an existing list
				//	request:
				//		See dojo.data.api.Read.fetch request

				return typeof request.query == "object";
			},
			clientSideFetch: function(/*Object*/ request,/*Array*/ baseResults){
				// summary:
				//		Performs a query on the client side and returns the results as an array
				//
				//	request:
				//		See dojo.data.api.Read.fetch request
				//
				//	baseResults:
				//		This provides the result set to start with for client side querying
				if(request.queryOptions && request.queryOptions.results){
					baseResults = request.queryOptions.results;
				}
				if(request.query){
					// filter by the query
					var results = [];
					for(var i = 0; i < baseResults.length; i++){
						var value = baseResults[i];
						if(value && this.matchesQuery(value,request)){
							results.push(baseResults[i]);
						}
					}
				}else{
					results = request.sort ? baseResults.concat() : baseResults; // we don't want to mutate the baseResults if we are doing a sort
				}
				if(request.sort){
					// do the sort if needed
					results.sort(this.makeComparator(request.sort.concat()));
				}
				return this.clientSidePaging(request, results);
			},
			clientSidePaging: function(/*Object*/ request,/*Array*/ baseResults){
				var start = request.start || 0;
				var finalResults = (start || request.count) ? baseResults.slice(start,start + (request.count || baseResults.length)) : baseResults;
				finalResults._fullLength = baseResults.length;
				return finalResults;
			},
			matchesQuery: function(item,request){
				var query = request.query;
				var ignoreCase = request.queryOptions && request.queryOptions.ignoreCase;
				for(var i in query){
					// if anything doesn't match, than this should be in the query
					var match = query[i];
					var value = this.getValue(item,i);
					if((typeof match == 'string' && (match.match(/[\*\.]/) || ignoreCase)) ?
						!dojo.data.util.filter.patternToRegExp(match, ignoreCase).test(value) :
						value != match){
						return false;
					}
				}
				return true;
			},
			makeComparator: function(sort){
				//	summary:
				//		returns a comparator function for the given sort order array
				//	sort:
				//		See dojox.data.api.Read.fetch
				var current = sort.shift();
				if(!current){
					// sort order for ties and no sort orders
					return function(){
						return 0;// keep the order unchanged
					};
				}
				var attribute = current.attribute;
				var descending = !!current.descending;
				var next = this.makeComparator(sort);
				var store = this;
				return function(a,b){
					var av = store.getValue(a,attribute);
					var bv = store.getValue(b,attribute);
					if(av != bv){
						return av < bv == descending ? 1 : -1;
					}
					return next(a,b);
				};
			}
		}
	);
	cf.onUpdate = function(){};

	return dojox.data.ClientFilter;
});
<|endoftext|>