<commit_before>var EventEmitter = require('events').EventEmitter;
var util = require('util');

var net = require('net');

function padding(str, fill, length) {
    while (str.length < length) {
        str = fill + str;
    }
    return str
}

function getId() {
    return padding(Math.floor(0xffffffff * Math.random()).toString(16), "0", 8)
}

/*
function SocketServer(ws) {
    this.webSocket = ws;
    this.socket = null
    this.authed = true;

    this.webSocket.on('set_target', this.onWebSocketSetTarget.bind(this));

    this.webSocket.on('FIN', this.onWebSocketFIN.bind(this));
    this.webSocket.on('up_stream', this.onWebSocketUpStream.bind(this));

}

SocketServer.prototype.onWebSocketSetTarget = function(target) {
    this.log('connectiong to' + target.address + ":" + target.port);

    if (!this.authed) {
        this.webSocket.emit('error', 'auth failed');
    }

    this.socket = net.(target.port, target.address, this.onSocketConnect.bind(this));

    this.socket.on('data', this.onSocketData.bind(this));
    this.socket.on('connect', this.onSocketConnect.bind(this));
    this.socket.on('end', this.onSocketEnd.bind(this));
    this.socket.on('close', this.onSocketClose.bind(this));
    this.socket.on('error', this.onSocketError.bind(this));
};

SocketServer.prototype.onWebSocketFIN = function() {
    this.wsClose();
    this.sClose();
}

SocketServer.prototype.onWebSocketUpStream = function(data) {
    this.log('sending data to dis server...');
    if (!this.socket) {
        this.wsError('not connected');
        return;
    }
    this.socket.write(new Buffer(data, 'base64'));
}

SocketServer.prototype.onSocketData = function(data) {
    dump(data);
    this.log('sending data to client...');
    this.webSocket.emit('down_stream', data.toString('base64'));
};

SocketServer.prototype.onSocketConnect = function() {
    this.log('connected to dis server.');
    this.webSocket.emit('target_setted');
};

SocketServer.prototype.onSocketClose = function() {
    this.wsClose();
    this.sClose();
};

SocketServer.prototype.onSocketEnd = function() {
    this.wsClose();
    this.sClose();
};

SocketServer.prototype.onSocketError = function() {
    this.wsError('error', 'lost connection');
    this.sClose();
};

SocketServer.prototype.wsClose = function() {
    this.webSocket.emit('FIN');
    this.wsDelayClose();
}

SocketServer.prototype.wsError = function(err) {
    this.webSocket.emit('error', 'err');
    this.wsDelayClose();
}

SocketServer.prototype.wsDelayClose =  function(err) {
    var self = this;
    self.log('waiting to stop ws connect...');
    setTimeout(function(){
        self.webSocket.disconnect();
    }, 10000)
}

SocketServer.prototype.sClose = function() {
    this.log('stop socket connect...');
    this.socket.end();
    this.socket.destroy();
}

SocketServer.prototype.log = function(msg) {
    if (this.debug) {
        console.log(msg);
    }
}

function dump(chunk){
  console.log('dumping:');
  console.log(chunk.toString('utf8'));

  var res = "";

  for (var i = 0; i < chunk.length; i++) {
    res += (chunk[i] > 15 ? "" : "0") + chunk[i].toString(16) + " ";
    if ((i + 1) % 8 === 0) {
        res += "\n";
    }
  }

  console.log(res);

}

module.exports = SocketServer;
*/

function SocketServer(ws) {
    this.webSocket = ws;
    this.sockets = [];
    this.socketMap = {};
    this.authed = true;

    this.sessionId = getId();

    this.timeout = 15 * 60 * 1000;
    this.lastPing = Date.now();

    this.debug = true;
    this.isAlive = true;

    this.webSocket.on('new_connection', this.onNewConnection.bind(this));
    this.webSocket.on('clear_connection', this.onClearConnection.bind(this));
    this.webSocket.on('client_ping', this.onClientPing.bind(this));

    this.timeoutListenerId = setInterval(this.checkTimeout.bind(this), this.timeout);
    this.onceConnect();
}

util.inherits(SocketServer, EventEmitter);

SocketServer.prototype.onceConnect = function onceConnect() {
    this.webSocket.emit('init_session', this.sessionId);
};

SocketServer.prototype.checkTimeout = function checkTimeout() {
    if (this.lastPing + this.timeout < Date.now()) {
        this.destroy();
    }
};

SocketServer.prototype.onClientPing = function onClientPing() {
    this.log('client ping!');
    this.lastPing = Date.now();
}

SocketServer.prototype.onNewConnection = function onNewConnection(data) {

    var id = data.sessionId;

    this.log(id + " : preparing connection toward " + data.address + ":" + data.port);

    var socket = net.createConnection(data.port, data.address);
    socket.sessionId = id;
    this.sockets.push(socket);
    this.socketMap[id] = socket;

    this.forwardEvent(id, "connect");
    this.forwardEvent(id, "data");
    this.forwardEvent(id, "end");
    this.forwardEvent(id, "drain");
    this.forwardEvent(id, "error");
    this.forwardEvent(id, "close");

    this.webSocket.on(id + '-client_write', this.onClientWrite.bind(this));
    this.webSocket.on(id + '-client_end', this.onClientEnd.bind(this));
    this.webSocket.on(id + '-client_destroy', this.onClientDestroy.bind(this));

    //console.log(id + '-client_write');
}

SocketServer.prototype.onClearConnection = function onClearConnection(id) {
    var socket, i;

    if (!this.socketMap[id]) {
        return false;
    }

    this.log(id + " : clearing connection");

    //make sure socket were destroyed
    try {
        this.socketMap[id].destroy();
    } catch (e) {}

    //make sure all reference were removed
    this.socketMap[id].removeAllListeners();

    socket = this.socketMap[id];
    delete this.socketMap[id];

    i = this.sockets.indexOf(socket);
    this.sockets.splice(i, 1);

}

SocketServer.prototype.onClientWrite = function onClientWrite(data) {
    data = this.unPack_(data);
    id = data.id;

    this.log(id + " : client write");
    try {
        this.socketMap[id].write(data.data);
    } catch (e) {
        console.log(data.data, e);
    }
}
SocketServer.prototype.onClientEnd = function onClientEnd(data) {
    data = this.unPack_(data);
    id = data.id;

    this.log(id + " : client end");

    this.socketMap[id].end(data.data);
}
SocketServer.prototype.onClientDestroy = function onClientDestroy(data) {
    data = this.unPack_(data);
    id = data.id;

    this.log(id + " : client destroy");

    //this.socketMap[id].destroy();
}

SocketServer.prototype.forwardEvent = function forwardEvent(id, type) {
    var self = this
    function callback () {
        var args = Array.prototype.slice.call(arguments, 0);
        self.log(id + " : forwarding event [" + type + "]");
        self.webSocket.emit.apply(
            self.webSocket,
            [id + "-" + type].concat(args)
        );
    }
    this.socketMap[id].on(type, callback);
}

SocketServer.prototype.log = function(str) {
    if (this.debug) {
        console.log(str);
    }
}

SocketServer.prototype.unPack_ = function (str) {
    var data = JSON.parse(str);
    if (data.data !== undefined) {
        data.data = new Buffer(data.data, 'base64');
    }
    return data;
}

SocketServer.prototype.destroy = function destroy() {
    clearInterval(this.timeoutListenerId);
    this.sockets.forEach(function(socket){
        try {
            socket.destroy();
            socket.removeAllListeners();
        } catch (e) {}
    });
    this.sockets = null;
    this.socketMap = null;
    this.webSocket.removeAllListeners();

    try {
        this.webSocket.disconnect(true);
    } catch (e) {
        console.log(e);
    }

    this.webSocket = null;

    console.log('shuting down connections');

    this.isAlive = false;
    this.emit('destroy');
}
module.exports = SocketServer;<commit_msg>fix bug which may cause crash<commit_after>var EventEmitter = require('events').EventEmitter;
var util = require('util');

var net = require('net');

function padding(str, fill, length) {
    while (str.length < length) {
        str = fill + str;
    }
    return str
}

function getId() {
    return padding(Math.floor(0xffffffff * Math.random()).toString(16), "0", 8)
}

/*
function SocketServer(ws) {
    this.webSocket = ws;
    this.socket = null
    this.authed = true;

    this.webSocket.on('set_target', this.onWebSocketSetTarget.bind(this));

    this.webSocket.on('FIN', this.onWebSocketFIN.bind(this));
    this.webSocket.on('up_stream', this.onWebSocketUpStream.bind(this));

}

SocketServer.prototype.onWebSocketSetTarget = function(target) {
    this.log('connectiong to' + target.address + ":" + target.port);

    if (!this.authed) {
        this.webSocket.emit('error', 'auth failed');
    }

    this.socket = net.(target.port, target.address, this.onSocketConnect.bind(this));

    this.socket.on('data', this.onSocketData.bind(this));
    this.socket.on('connect', this.onSocketConnect.bind(this));
    this.socket.on('end', this.onSocketEnd.bind(this));
    this.socket.on('close', this.onSocketClose.bind(this));
    this.socket.on('error', this.onSocketError.bind(this));
};

SocketServer.prototype.onWebSocketFIN = function() {
    this.wsClose();
    this.sClose();
}

SocketServer.prototype.onWebSocketUpStream = function(data) {
    this.log('sending data to dis server...');
    if (!this.socket) {
        this.wsError('not connected');
        return;
    }
    this.socket.write(new Buffer(data, 'base64'));
}

SocketServer.prototype.onSocketData = function(data) {
    dump(data);
    this.log('sending data to client...');
    this.webSocket.emit('down_stream', data.toString('base64'));
};

SocketServer.prototype.onSocketConnect = function() {
    this.log('connected to dis server.');
    this.webSocket.emit('target_setted');
};

SocketServer.prototype.onSocketClose = function() {
    this.wsClose();
    this.sClose();
};

SocketServer.prototype.onSocketEnd = function() {
    this.wsClose();
    this.sClose();
};

SocketServer.prototype.onSocketError = function() {
    this.wsError('error', 'lost connection');
    this.sClose();
};

SocketServer.prototype.wsClose = function() {
    this.webSocket.emit('FIN');
    this.wsDelayClose();
}

SocketServer.prototype.wsError = function(err) {
    this.webSocket.emit('error', 'err');
    this.wsDelayClose();
}

SocketServer.prototype.wsDelayClose =  function(err) {
    var self = this;
    self.log('waiting to stop ws connect...');
    setTimeout(function(){
        self.webSocket.disconnect();
    }, 10000)
}

SocketServer.prototype.sClose = function() {
    this.log('stop socket connect...');
    this.socket.end();
    this.socket.destroy();
}

SocketServer.prototype.log = function(msg) {
    if (this.debug) {
        console.log(msg);
    }
}

function dump(chunk){
  console.log('dumping:');
  console.log(chunk.toString('utf8'));

  var res = "";

  for (var i = 0; i < chunk.length; i++) {
    res += (chunk[i] > 15 ? "" : "0") + chunk[i].toString(16) + " ";
    if ((i + 1) % 8 === 0) {
        res += "\n";
    }
  }

  console.log(res);

}

module.exports = SocketServer;
*/

function SocketServer(ws) {
    this.webSocket = ws;
    this.sockets = [];
    this.socketMap = {};
    this.authed = true;

    this.sessionId = getId();

    this.timeout = 15 * 60 * 1000;
    this.lastPing = Date.now();

    this.debug = true;
    this.isAlive = true;

    this.webSocket.on('new_connection', this.onNewConnection.bind(this));
    this.webSocket.on('clear_connection', this.onClearConnection.bind(this));
    this.webSocket.on('client_ping', this.onClientPing.bind(this));

    this.timeoutListenerId = setInterval(this.checkTimeout.bind(this), this.timeout);
    this.onceConnect();
}

util.inherits(SocketServer, EventEmitter);

SocketServer.prototype.onceConnect = function onceConnect() {
    this.webSocket.emit('init_session', this.sessionId);
};

SocketServer.prototype.checkTimeout = function checkTimeout() {
    if (this.lastPing + this.timeout < Date.now()) {
        this.destroy();
    }
};

SocketServer.prototype.onClientPing = function onClientPing() {
    this.log('client ping!');
    this.lastPing = Date.now();
}

SocketServer.prototype.onNewConnection = function onNewConnection(data) {

    var id = data.sessionId;

    this.log(id + " : preparing connection toward " + data.address + ":" + data.port);

    var socket = net.createConnection(data.port, data.address);
    socket.sessionId = id;
    this.sockets.push(socket);
    this.socketMap[id] = socket;

    this.forwardEvent(id, "connect");
    this.forwardEvent(id, "data");
    this.forwardEvent(id, "end");
    this.forwardEvent(id, "drain");
    this.forwardEvent(id, "error");
    this.forwardEvent(id, "close");

    this.webSocket.on(id + '-client_write', this.onClientWrite.bind(this));
    this.webSocket.on(id + '-client_end', this.onClientEnd.bind(this));
    this.webSocket.on(id + '-client_destroy', this.onClientDestroy.bind(this));

    //console.log(id + '-client_write');
}

SocketServer.prototype.onClearConnection = function onClearConnection(id) {
    var socket, i;

    if (!this.socketMap[id]) {
        return false;
    }

    this.log(id + " : clearing connection");

    //make sure socket were destroyed
    try {
        this.socketMap[id].destroy();
    } catch (e) {}

    //make sure all reference were removed
    this.socketMap[id].removeAllListeners();

    socket = this.socketMap[id];
    delete this.socketMap[id];

    i = this.sockets.indexOf(socket);
    this.sockets.splice(i, 1);

}

SocketServer.prototype.onClientWrite = function onClientWrite(data) {
    data = this.unPack_(data);
    id = data.id;

    this.log(id + " : client write");
    try {
        this.socketMap[id].write(data.data);
    } catch (e) {
        console.log(data.data, e);
    }
}
SocketServer.prototype.onClientEnd = function onClientEnd(data) {
    data = this.unPack_(data);
    id = data.id;

    this.log(id + " : client end");

    this.socketMap[id].end(data.data);
}
SocketServer.prototype.onClientDestroy = function onClientDestroy(data) {
    data = this.unPack_(data);
    id = data.id;

    this.log(id + " : client destroy");

    //this.socketMap[id].destroy();
}

SocketServer.prototype.forwardEvent = function forwardEvent(id, type) {
    var self = this
    function callback () {
        var args = Array.prototype.slice.call(arguments, 0);
        self.log(id + " : forwarding event [" + type + "]");
        self.webSocket.emit.apply(
            self.webSocket,
            [id + "-" + type].concat(args)
        );
    }
    this.socketMap[id].on(type, callback);
}

SocketServer.prototype.log = function(str) {
    if (this.debug) {
        console.log(str);
    }
}

SocketServer.prototype.unPack_ = function (str) {
    var data = JSON.parse(str);
    if (data.data !== undefined) {
        data.data = new Buffer(data.data, 'base64');
    }
    return data;
}

SocketServer.prototype.destroy = function destroy() {
    clearInterval(this.timeoutListenerId);
    this.sockets.forEach(function(socket){
        try {
            socket.destroy();
        } catch (e) {}
        try {
            socket.removeAllListeners();
        } catch (e) {}
    });
    this.sockets = null;
    this.socketMap = null;
    this.webSocket.removeAllListeners();

    try {
        this.webSocket.disconnect(true);
    } catch (e) {
        console.log(e);
    }

    this.webSocket = null;

    console.log('shuting down connections');

    this.isAlive = false;
    this.emit('destroy');
}
module.exports = SocketServer;<|endoftext|>


<commit_before>'use strict';

const defaultGame = require('../js/shared/defaultGame');
const describe = require('mocha').describe;
const expect = require('expect');
const GameHelpers = require('../js/shared/GameHelpers');
const Immutable = require('immutable');
const it = require('mocha').it;

describe('GameHelpers', () => {
  it('getNextWordIndex should work', () => {
    let game = defaultGame;

    // No words available
    expect(() => {
      GameHelpers.getNextWordIndex(game);
    }).toThrow(Error);

    game = game.update('words', (words) => {
      return words
        .push(Immutable.fromJS({word: 'word0', index: 0, inBowl:
        .push(Immutable.fromJS({word: 'word1', index: 1, inBowl: false}));
    });

    // Still no words in bowl
    expect(() => {
      GameHelpers.getNextWordIndex(game);
    }).toThrow(Error); // no words available

    game = game.update('words', (words) => {
      return words
        .push(Immutable.fromJS({word: 'word2', index: 2, inBowl: true}))
        .push(Immutable.fromJS({word: 'word3', index: 3, inBowl: false}))
        .push(Immutable.fromJS({word: 'word4', index: 4, inBowl: false}));
    });

    // there is only one word in the bowl so it should be chosen
    expect(GameHelpers.getNextWordIndex(game)).toEqual(2);
  });

  it('playerIsJoined should work', () => {
    let game = defaultGame;
    const userId = 'userId';
    expect(GameHelpers.playerIsJoined(game, userId)).toEqual(false);
    game = game.set('userId', userId);
    expect(GameHelpers.playerIsJoined(game, userId)).toEqual(false);
    game = game.set('players', game.get('players').push(Immutable.fromJS({id: 'notUserId'})));
    expect(GameHelpers.playerIsJoined(game, userId)).toEqual(false);
    game = game.set('players', game.get('players').push(Immutable.fromJS({id: userId})));
    expect(GameHelpers.playerIsJoined(game, userId)).toEqual(true);
  });

  it('getTeams should work', () => {
    let game = defaultGame
      .set('id', 'testid');
    const player1 = Immutable.fromJS({id: 'player1', team: 0});
    const player2 = Immutable.fromJS({id: 'player2', team: 0});
    const player3 = Immutable.fromJS({id: 'player3', team: 0});
    const player4 = Immutable.fromJS({id: 'player4', team: 0});
    const player5 = Immutable.fromJS({id: 'player5', team: 1});
    const player6 = Immutable.fromJS({id: 'player6', team: 1});
    game = game.set('players', game.get('players').push(player1, player2, player3, player4, player5, player6));
    expect(GameHelpers.getTeams(game).size).toEqual(2, 'There should be 2 teams');
    expect(GameHelpers.getTeams(game).get(0).get('players').size).toEqual(4, 'Team one should have 4 players');
    expect(GameHelpers.getTeams(game).get(1).get('players').size).toEqual(2, 'Team two should have 4 players');
  });

  it('getCurrentPlayer should work');

  it('getCurrentTeam should work');

  it('playerIsGuessing should work');

  it('getPlayerWords should work', () => {
    let game = defaultGame;
    const userId = 'testId';
    const word1 = Immutable.fromJS({playerId: userId, word: 'word1'});
    const word2 = Immutable.fromJS({playerId: userId, word: 'word2'});
    const word3 = Immutable.fromJS({playerId: 'someOtherPlayer', word: 'word3'});

    game = game.set('userId', userId);
    expect(GameHelpers.getPlayerWords(game, userId).size).toEqual(0);

    game = game.set('words', game.get('words').push(word1));
    expect(GameHelpers.getPlayerWords(game, userId).size).toEqual(1);
    expect(GameHelpers.getPlayerWords(game, userId).get(0).get('word')).toEqual('word1');

    game = game.set('words', game.get('words').push(word2));
    expect(GameHelpers.getPlayerWords(game, userId).size).toEqual(2);

    game = game.set('words', game.get('words').push(word3));
    expect(GameHelpers.getPlayerWords(game, userId).size).toEqual(2);
  });

  it('playerWordsAreValid should work');

  it('getPlayerIndex should work', () => {
    let game = defaultGame
      .set('userId', 'p2')
      .update('players', (players) => {
        return players
          .push(Immutable.fromJS({id: 'p1'}))
          .push(Immutable.fromJS({id: 'p2'}))
          .push(Immutable.fromJS({id: 'p3'}));
      });

    expect(GameHelpers.getPlayerIndex(game, 'p1')).toEqual(0);
    expect(GameHelpers.getPlayerIndex(game, 'p2')).toEqual(1);
    expect(GameHelpers.getPlayerIndex(game, 'p3')).toEqual(2);
  });

  it('readyToStart should work', () => {
    let game = defaultGame.set('id', 'testgame');
    expect(GameHelpers.readyToStart(game)).toEqual(false);
    game = game.update('words', (words) => {
      return words
        .push(Immutable.fromJS({word: 'word1', inBowl: true}))
        .push(Immutable.fromJS({word: 'word2', inBowl: true}))
        .push(Immutable.fromJS({word: 'word3', inBowl: true}))
        .push(Immutable.fromJS({word: 'word4', inBowl: true}));
    });
    expect(GameHelpers.readyToStart(game)).toEqual(false);
    game = game.update('players', (players) => {
      return players
        .push(Immutable.fromJS({id: 'p1', team: 0}))
        .push(Immutable.fromJS({id: 'p2', team: 0}))
        .push(Immutable.fromJS({id: 'p3', team: 1}))
        .push(Immutable.fromJS({id: 'p4', team: 1}));
    });
    expect(GameHelpers.readyToStart(game)).toEqual(true);
  });

  it('getMostSkippedWord should work', () => {
    const word1 = Immutable.fromJS({word: 'word1', skips: 3});
    const word2 = Immutable.fromJS({word: 'word2', skips: 5});
    const word3 = Immutable.fromJS({word: 'word3', skips: 1});
    const game = defaultGame.set('words', defaultGame.get('words').push(word1, word2, word3));

    expect(GameHelpers.getMostSkippedWord(game)).toEqual(word2);
    expect(GameHelpers.getMostSkippedWord(game).get('word')).toEqual('word2');
  });

});
<commit_msg>Fixed typos/bad merge/something in unit test.<commit_after>'use strict';

const defaultGame = require('../js/shared/defaultGame');
const describe = require('mocha').describe;
const expect = require('expect');
const GameHelpers = require('../js/shared/GameHelpers');
const Immutable = require('immutable');
const it = require('mocha').it;

describe('GameHelpers', () => {
  it('getNextWordIndex should work', () => {
    let game = defaultGame;

    // No words available
    expect(() => {
      GameHelpers.getNextWordIndex(game);
    }).toThrow(Error);

    game = game.set('words', game.get('words')
      .push(Immutable.fromJS({word: 'word0', index: 0, inBowl: false}))
      .push(Immutable.fromJS({word: 'word1', index: 1, inBowl: false})));

    // Still no words in bowl
    expect(() => {
      GameHelpers.getNextWordIndex(game);
    }).toThrow(Error); // no words available

    game = game.set('words', game.get('words')
      .push(Immutable.fromJS({word: 'word2', index: 2, inBowl: true}))
      .push(Immutable.fromJS({word: 'word3', index: 3, inBowl: false}))
      .push(Immutable.fromJS({word: 'word4', index: 4, inBowl: false})));

    // there is only one word in the bowl so it should be chosen
    expect(GameHelpers.getNextWordIndex(game)).toEqual(2);
  });

  it('playerIsJoined should work', () => {
    let game = defaultGame;
    const userId = 'userId';
    expect(GameHelpers.playerIsJoined(game, userId)).toEqual(false);
    game = game.set('userId', userId);
    expect(GameHelpers.playerIsJoined(game, userId)).toEqual(false);
    game = game.set('players', game.get('players').push(Immutable.fromJS({id: 'notUserId'})));
    expect(GameHelpers.playerIsJoined(game, userId)).toEqual(false);
    game = game.set('players', game.get('players').push(Immutable.fromJS({id: userId})));
    expect(GameHelpers.playerIsJoined(game, userId)).toEqual(true);
  });

  it('getTeams should work', () => {
    let game = defaultGame
      .set('id', 'testid');
    const player1 = Immutable.fromJS({id: 'player1', team: 0});
    const player2 = Immutable.fromJS({id: 'player2', team: 0});
    const player3 = Immutable.fromJS({id: 'player3', team: 0});
    const player4 = Immutable.fromJS({id: 'player4', team: 0});
    const player5 = Immutable.fromJS({id: 'player5', team: 1});
    const player6 = Immutable.fromJS({id: 'player6', team: 1});
    game = game.set('players', game.get('players').push(player1, player2, player3, player4, player5, player6));
    expect(GameHelpers.getTeams(game).size).toEqual(2, 'There should be 2 teams');
    expect(GameHelpers.getTeams(game).get(0).get('players').size).toEqual(4, 'Team one should have 4 players');
    expect(GameHelpers.getTeams(game).get(1).get('players').size).toEqual(2, 'Team two should have 4 players');
  });

  it('getCurrentPlayer should work');

  it('getCurrentTeam should work');

  it('playerIsGuessing should work');

  it('getPlayerWords should work', () => {
    let game = defaultGame;
    const userId = 'testId';
    const word1 = Immutable.fromJS({playerId: userId, word: 'word1'});
    const word2 = Immutable.fromJS({playerId: userId, word: 'word2'});
    const word3 = Immutable.fromJS({playerId: 'someOtherPlayer', word: 'word3'});

    game = game.set('userId', userId);
    expect(GameHelpers.getPlayerWords(game, userId).size).toEqual(0);

    game = game.set('words', game.get('words').push(word1));
    expect(GameHelpers.getPlayerWords(game, userId).size).toEqual(1);
    expect(GameHelpers.getPlayerWords(game, userId).get(0).get('word')).toEqual('word1');

    game = game.set('words', game.get('words').push(word2));
    expect(GameHelpers.getPlayerWords(game, userId).size).toEqual(2);

    game = game.set('words', game.get('words').push(word3));
    expect(GameHelpers.getPlayerWords(game, userId).size).toEqual(2);
  });

  it('playerWordsAreValid should work');

  it('getPlayerIndex should work', () => {
    let game = defaultGame
      .set('userId', 'p2')
      .update('players', (players) => {
        return players
          .push(Immutable.fromJS({id: 'p1'}))
          .push(Immutable.fromJS({id: 'p2'}))
          .push(Immutable.fromJS({id: 'p3'}));
      });

    expect(GameHelpers.getPlayerIndex(game, 'p1')).toEqual(0);
    expect(GameHelpers.getPlayerIndex(game, 'p2')).toEqual(1);
    expect(GameHelpers.getPlayerIndex(game, 'p3')).toEqual(2);
  });

  it('readyToStart should work', () => {
    let game = defaultGame.set('id', 'testgame');
    expect(GameHelpers.readyToStart(game)).toEqual(false);
    game = game.update('words', (words) => {
      return words
        .push(Immutable.fromJS({word: 'word1', inBowl: true}))
        .push(Immutable.fromJS({word: 'word2', inBowl: true}))
        .push(Immutable.fromJS({word: 'word3', inBowl: true}))
        .push(Immutable.fromJS({word: 'word4', inBowl: true}));
    });
    expect(GameHelpers.readyToStart(game)).toEqual(false);
    game = game.update('players', (players) => {
      return players
        .push(Immutable.fromJS({id: 'p1', team: 0}))
        .push(Immutable.fromJS({id: 'p2', team: 0}))
        .push(Immutable.fromJS({id: 'p3', team: 1}))
        .push(Immutable.fromJS({id: 'p4', team: 1}));
    });
    expect(GameHelpers.readyToStart(game)).toEqual(true);
  });

  it('getMostSkippedWord should work', () => {
    const word1 = Immutable.fromJS({word: 'word1', skips: 3});
    const word2 = Immutable.fromJS({word: 'word2', skips: 5});
    const word3 = Immutable.fromJS({word: 'word3', skips: 1});
    const game = defaultGame.set('words', defaultGame.get('words').push(word1, word2, word3));

    expect(GameHelpers.getMostSkippedWord(game)).toEqual(word2);
    expect(GameHelpers.getMostSkippedWord(game).get('word')).toEqual('word2');
  });

});
<|endoftext|>


<commit_before>var ocr = require('../controllers/ocr.js');
var should = require('should');
var assert = require('assert');
var expect = require('expect.js');

describe('Image-to-text conversion', function(){
  var path =  '../../client/assets/1/';
  it('should return error if invalid path is input', function(done){
    url = 'fakePath';
    ocr.convertImageToText(path + url, function(error, text){
      expect(!!error).to.equal(true);
      done();
    });
  });

  it('should not return anything when image, without text, is processed', function(done){
    url = 'www_google_com_fonts.jpg';
    ocr.convertImageToText(path + url, function(error, text){
     console.log(error);
     expect(error).to.equal(null);
     expect(text).to.equal('');
     done();
   });
  });

  it('should not return anything when image, without text, is processed', function(done){
    url = 'www_facebook_com.jpg';
    ocr.convertImageToText(path + url, function(error, text){
     expect(error).to.equal(null);
     expect(text).to.equal('Connect with friends and the world around you on Facebook.');
     done();
   });
  });

  it('should text convert an that reads "Open Sans" as such', function(done){
    url = 'www_fontsquirrel_com_fonts_list_popular.jpg';
    ocr.convertImageToText(path + url, function(error, text){
     expect(error).to.equal(null);
     expect(text).to.equal('Open Sans');
     done();
    });
  });

  it('should text convert an that reads "Open Sans" as such', function(done){
    url = 'www_google_com.jpg';
    ocr.convertImageToText(path + url, function(error, text){
     expect(error).to.equal(null);
     expect(text).to.equal('A faster way to browse the web');
     done();
   });
  });

});
<commit_msg>[Feature] added OCR with working tests<commit_after>var ocr = require('../controllers/ocr.js');
var should = require('should');
var assert = require('assert');
var expect = require('expect.js');

//works!
describe('Image-to-text conversion', function(){
  var path =  '../../client/assets/1/';
  it('should return error if invalid path is input', function(done){
    url = 'fakePath';
    ocr.convertImageToText(path + url, function(error, text){
      expect(!!error).to.equal(true);
      done();
    });
  });

  it('should not return anything when image, without text, is processed', function(done){
    url = 'www_google_com_fonts.jpg';
    ocr.convertImageToText(path + url, function(error, text){
     console.log(error);
     expect(error).to.equal(null);
     expect(text).to.equal('');
     done();
   });
  });

  it('should not return anything when image, without text, is processed', function(done){
    url = 'www_facebook_com.jpg';
    ocr.convertImageToText(path + url, function(error, text){
     expect(error).to.equal(null);
     expect(text).to.equal('Connect with friends and the world around you on Facebook.');
     done();
   });
  });

  it('should text convert an that reads "Open Sans" as such', function(done){
    url = 'www_fontsquirrel_com_fonts_list_popular.jpg';
    ocr.convertImageToText(path + url, function(error, text){
     expect(error).to.equal(null);
     expect(text).to.equal('Open Sans');
     done();
    });
  });

  it('should text convert an that reads "Open Sans" as such', function(done){
    url = 'www_google_com.jpg';
    ocr.convertImageToText(path + url, function(error, text){
     expect(error).to.equal(null);
     expect(text).to.equal('A faster way to browse the web');
     done();
   });
  });

});
<|endoftext|>


<commit_before>import { EMPTYO, EMPTYAR, isArray, isVNode } from "./utils";

export function h(type, props, contArg) {
  var content;
  var len = arguments.length - 2;

  if (len === 1) {
    if (isArray(contArg)) {
      content = maybeFlatten(contArg);
    } else if (isVNode(contArg)) {
      content = [contArg];
    } else {
      content = [{ _text: contArg == null ? "" : contArg }];
    }
  } else if (len > 1) {
    var args = Array(len);
    for (var i = 0; i < len; i++) {
      args[i] = arguments[i + 2];
    }
    content = maybeFlatten(args);
  } else {
    content = EMPTYAR;
  }

  return {
    _vnode: true,
    type,
    key: (props && props.key) || null,
    props: props || EMPTYO,
    content
  };
}

export function maybeFlatten(arr) {
  for (var i = 0; i < arr.length; i++) {
    var ch = arr[i];
    if (isArray(ch)) {
      return flattenChildren(arr, i, arr.slice(0, i));
    } else if (!isVNode(ch)) {
      arr[i] = { _text: ch == null ? "" : ch };
    }
  }
  return arr;
}

function flattenChildren(children, start, arr) {
  for (var i = start; i < children.length; i++) {
    var ch = children[i];
    if (isArray(ch)) {
      flattenChildren(ch, 0, arr);
    } else if (isVNode(ch)) {
      arr.push(ch);
    } else {
      arr.push({ _text: ch == null ? "" : ch });
    }
  }
  return arr;
}
<commit_msg>fix normalization for comp types<commit_after>import { EMPTYO, EMPTYAR, isArray, isVNode } from "./utils";

export function h(type, props, contArg) {
  var content, args, i;
  var len = arguments.length - 2;

  if (typeof type !== "string") {
    if (len === 1) {
      content = contArg;
    } else if (len > 1) {
      args = Array(len);
      for (i = 0; i < len; i++) {
        args[i] = arguments[i + 2];
      }
      content = contArg;
    }
  } else {
    if (len === 1) {
      if (isArray(contArg)) {
        content = maybeFlatten(contArg);
      } else if (isVNode(contArg)) {
        content = [contArg];
      } else {
        content = [{ _text: contArg == null ? "" : contArg }];
      }
    } else if (len > 1) {
      args = Array(len);
      for (i = 0; i < len; i++) {
        args[i] = arguments[i + 2];
      }
      content = maybeFlatten(args);
    } else {
      content = EMPTYAR;
    }
  }

  return {
    _vnode: true,
    type,
    key: (props && props.key) || null,
    props: props || EMPTYO,
    content
  };
}

export function maybeFlatten(arr) {
  for (var i = 0; i < arr.length; i++) {
    var ch = arr[i];
    if (isArray(ch)) {
      return flattenChildren(arr, i, arr.slice(0, i));
    } else if (!isVNode(ch)) {
      arr[i] = { _text: ch == null ? "" : ch };
    }
  }
  return arr;
}

function flattenChildren(children, start, arr) {
  for (var i = start; i < children.length; i++) {
    var ch = children[i];
    if (isArray(ch)) {
      flattenChildren(ch, 0, arr);
    } else if (isVNode(ch)) {
      arr.push(ch);
    } else {
      arr.push({ _text: ch == null ? "" : ch });
    }
  }
  return arr;
}
<|endoftext|>


<commit_before>var should = require("should");
var webpack = require("../lib/webpack");
var WebpackOptionsValidationError = require("../lib/WebpackOptionsValidationError");

describe("Validation", function() {
	var testCases = [{
		name: "undefined configuration",
		config: undefined,
		message: [
			" - configuration should be an object."
		]
	}, {
		name: "null configuration",
		config: null,
		message: [
			" - configuration should be an object."
		]
	}, {
		name: "empty configuration",
		config: {},
		message: [
			" - configuration misses the property 'entry'.",
			"   object { <key>: string | [string] } | string | [string]",
			"   The entry point(s) of the compilation."
		]
	}, {
		name: "empty entry string",
		config: {
			entry: ""
		},
		message: [
			" - configuration.entry should be one of these:",
			"   object { <key>: string | [string] } | string | [string]",
			"   The entry point(s) of the compilation."
		]
	}, {
		name: "multiple errors",
		config: {
			entry: [/a/],
			output: {
				filename: /a/
			}
		},
		message: [
			" - configuration.entry[0] should be a string.",
			" - configuration.entry should be one of these:",
			"   object { <key>: string | [string] } | string | [string]",
			"   The entry point(s) of the compilation.",
			" - configuration.output.filename should be a string."
		]
	}, {
		name: "multiple configurations",
		config: [{
			entry: [/a/],
		}, {
			entry: "a",
			output: {
				filename: /a/
			}
		}],
		message: [
			" - configuration[0].entry[0] should be a string.",
			" - configuration[0].entry should be one of these:",
			"   object { <key>: string | [string] } | string | [string]",
			"   The entry point(s) of the compilation.",
			" - configuration[1].output.filename should be a string."
		]
	}, {
		name: "deep error",
		config: {
			entry: "a",
			module: {
				rules: [{
					oneOf: [{
						test: "a",
						paser: {
							amd: false
						}
					}]
				}]
			}
		},
		message: [
			" - configuration.module.rules[0].oneOf[0] has an unknown property 'paser'. These properties are valid:",
			"   object { enforce?, exclude?, include?, issuer?, loader?, loaders?, oneOf?, options?, parser?, query?, resource?, rules?, test?, use? }"
		]
	}, {
		name: "additional key on root",
		config: {
			entry: "a",
			postcss: function() {}
		},
		message: [
			" - configuration has an unknown property 'postcss'. These properties are valid:",
			"   object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry, externals?, loader?, module?, name?, node?, output?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, stats?, target?, watch?, watchOptions? }",
			"   For typos: please correct them.",
			"   For loader options: webpack 2 no longer allows custom properties in configuration.",
			"     Loaders should be updated to allow passing options via loader options in module.rules.",
			"     Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:",
			"     plugins: {",
			"       new webpack.LoaderOptionsPlugin({",
			"         options: {",
			"           postcss: ...",
			"         }",
			"       })",
			"     }"
		]
	}];
	testCases.forEach(function(testCase) {
		it("should fail validation for " + testCase.name, function() {
			try {
				webpack(testCase.config);
			} catch(e) {
				if(!(e instanceof WebpackOptionsValidationError))
					throw e;
				e.message.should.startWith("Invalid configuration object.");
				e.message.split("\n").slice(1).should.be.eql(testCase.message);
				return;
			}
			throw new Error("Validation didn't fail");
		})
	});
});
<commit_msg>fixed max line length<commit_after>var should = require("should");
var webpack = require("../lib/webpack");
var WebpackOptionsValidationError = require("../lib/WebpackOptionsValidationError");

describe("Validation", function() {
	var testCases = [{
		name: "undefined configuration",
		config: undefined,
		message: [
			" - configuration should be an object."
		]
	}, {
		name: "null configuration",
		config: null,
		message: [
			" - configuration should be an object."
		]
	}, {
		name: "empty configuration",
		config: {},
		message: [
			" - configuration misses the property 'entry'.",
			"   object { <key>: string | [string] } | string | [string]",
			"   The entry point(s) of the compilation."
		]
	}, {
		name: "empty entry string",
		config: {
			entry: ""
		},
		message: [
			" - configuration.entry should be one of these:",
			"   object { <key>: string | [string] } | string | [string]",
			"   The entry point(s) of the compilation."
		]
	}, {
		name: "multiple errors",
		config: {
			entry: [/a/],
			output: {
				filename: /a/
			}
		},
		message: [
			" - configuration.entry[0] should be a string.",
			" - configuration.entry should be one of these:",
			"   object { <key>: string | [string] } | string | [string]",
			"   The entry point(s) of the compilation.",
			" - configuration.output.filename should be a string."
		]
	}, {
		name: "multiple configurations",
		config: [{
			entry: [/a/],
		}, {
			entry: "a",
			output: {
				filename: /a/
			}
		}],
		message: [
			" - configuration[0].entry[0] should be a string.",
			" - configuration[0].entry should be one of these:",
			"   object { <key>: string | [string] } | string | [string]",
			"   The entry point(s) of the compilation.",
			" - configuration[1].output.filename should be a string."
		]
	}, {
		name: "deep error",
		config: {
			entry: "a",
			module: {
				rules: [{
					oneOf: [{
						test: "a",
						paser: {
							amd: false
						}
					}]
				}]
			}
		},
		message: [
			" - configuration.module.rules[0].oneOf[0] has an unknown property 'paser'. These properties are valid:",
			"   object { enforce?, exclude?, include?, issuer?, loader?, loaders?, oneOf?, options?, parser?, query?, resource?, rules?, test?, use? }"
		]
	}, {
		name: "additional key on root",
		config: {
			entry: "a",
			postcss: function() {}
		},
		message: [
			" - configuration has an unknown property 'postcss'. These properties are valid:",
			"   object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry, externals?, " +
				"loader?, module?, name?, node?, output?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, " +
				"recordsPath?, resolve?, resolveLoader?, stats?, target?, watch?, watchOptions? }",
			"   For typos: please correct them.",
			"   For loader options: webpack 2 no longer allows custom properties in configuration.",
			"     Loaders should be updated to allow passing options via loader options in module.rules.",
			"     Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:",
			"     plugins: {",
			"       new webpack.LoaderOptionsPlugin({",
			"         options: {",
			"           postcss: ...",
			"         }",
			"       })",
			"     }"
		]
	}];
	testCases.forEach(function(testCase) {
		it("should fail validation for " + testCase.name, function() {
			try {
				webpack(testCase.config);
			} catch(e) {
				if(!(e instanceof WebpackOptionsValidationError))
					throw e;
				e.message.should.startWith("Invalid configuration object.");
				e.message.split("\n").slice(1).should.be.eql(testCase.message);
				return;
			}
			throw new Error("Validation didn't fail");
		})
	});
});
<|endoftext|>


<commit_before>let vscode = require( 'vscode' );

module.exports = {
    getContent( editor ) {
        let doc = editor.document,
            rng = new vscode.Range( 0, 0, doc.lineCount - 1, doc.lineAt( doc.lineCount - 1 ).text.length )

        return doc.getText( rng );
    },

    getContentWithSelections( editor ) {
        let ret = this.getContent( editor );

        for ( let sel of editor.selections.reverse() ) {
            ret = this._replaceSelection( ret, sel, editor.document );
        }

        return ret;
    },

    _replaceSelection( content, sel, doc ) {
        if ( sel.isEmpty ) {
            let selOffset = doc.offsetAt( sel.start );
            content = content.substr( 0, selOffset ) + '^' + content.substr( selOffset );
        } else {
            let startOffset = doc.offsetAt( sel.start ),
                endOffset = doc.offsetAt( sel.end ),
                startMarker = sel.start.isEqual( sel.active ) ? '{' : '[',
                endMarker = sel.end.isEqual( sel.active ) ? '}' : ']';

            content = content.substr( 0, startOffset ) + startMarker +
                content.substring( startOffset, endOffset ) + endMarker +
                content.substr( endOffset );
        }

        return content;
    }
}<commit_msg>Added docs.<commit_after>let vscode = require( 'vscode' );

module.exports = {
    /**
     * Returns content of a given editor.
     *
     * @param {TextEditor} editor
     * @returns {String}
     */
    getContent( editor ) {
        let doc = editor.document,
            rng = new vscode.Range( 0, 0, doc.lineCount - 1, doc.lineAt( doc.lineCount - 1 ).text.length )

        return doc.getText( rng );
    },

    /**
     * Returns content of a given editor as a string. Selection are marked with special characters:
     *
     * * Collapsed selections are marked with `^`
     * * Ranged selections are marked with
     *  * `[` or `]` for **anchor** position, based whether it's a beginning or the end of range,
     *  * `{` or `}` for **active** position, based whether it's a beginning or the end of range.
     *
     * In case of ranged selections, **anchor** is basically a position where you started the range.
     *
     * It supports multiple selections.
     *
     * **Known issues**
     * * Currently it will mess up if there are multiple ranged selections that overlaps with each other. It
     * was designed simplification.
     *
     * @param {TextEditor} editor
     * @returns {String}
     */
    getContentWithSelections( editor ) {
        let ret = this.getContent( editor );

        for ( let sel of editor.selections.reverse() ) {
            ret = this._replaceSelection( ret, sel, editor.document );
        }

        return ret;
    },

    _replaceSelection( content, sel, doc ) {
        if ( sel.isEmpty ) {
            let selOffset = doc.offsetAt( sel.start );
            content = content.substr( 0, selOffset ) + '^' + content.substr( selOffset );
        } else {
            let startOffset = doc.offsetAt( sel.start ),
                endOffset = doc.offsetAt( sel.end ),
                startMarker = sel.start.isEqual( sel.active ) ? '{' : '[',
                endMarker = sel.end.isEqual( sel.active ) ? '}' : ']';

            content = content.substr( 0, startOffset ) + startMarker +
                content.substring( startOffset, endOffset ) + endMarker +
                content.substr( endOffset );
        }

        return content;
    }
}<|endoftext|>


<commit_before>var path          = require('path');
var fs            = require('fs');
var os            = require('os');
var crypto        = require('crypto');
var should        = require('chai').should();
var expect        = require('chai').expect;
var avisynth      = require('../main');
var Script        = require('../code/script');
var loader        = require('../code/loader');
var pluginSystem  = require('../code/plugins');
var AvisynthError = require('../code/errors').AvisynthError;

describe('avisynth.Script', function() {
    var baseRefs = loader.references;
    var fakePluginsDir = path.resolve(__dirname, 'plugins');
    var scriptPath = path.resolve(fakePluginsDir, 'colors_rgb.avsi');
    var pluginPath = path.resolve(fakePluginsDir, 'DeDup.dll');
    var textFile   = path.resolve(fakePluginsDir, 'colors_rgb.txt');
    var missing    = path.resolve(fakePluginsDir, 'non-existent.dll');
    var rand = Math.random(); // Guess what, initializing it takes ~260ms for me on Win7.

    it('should be a Script construtor', function() {
        avisynth.Script.should.be.a('function');
        (new avisynth.Script).should.be.instanceof(Script);
    });

    it('should accept omitting the "new" operator', function() {
        avisynth.Script().should.be.instanceof(Script);
    });

    describe('instances', function() {
        it('should accept a code parameter, and include it as a property', function() {
            var code = '\nVersion()\n\nSubtitle("' + rand + '")';
            avisynth.Script(code).rawCode.should.equal(code);
        });

        it('should have empty code if no code was provided', function() {
            avisynth.Script().rawCode.should.equal('');
        });

        it('should integrate with the plugins by prototypal inheritance', function() {
            var script = new avisynth.Script();
            Object.getPrototypeOf(script).should.equal(pluginSystem.pluginPrototype);
        });

        describe('.load', function() {
            it('should load scripts and plugins from given paths', function() {
                var script = avisynth.Script();
                script.load(scriptPath);
                script.load(pluginPath);
                script.references[scriptPath].should.equal('script');
                script.references[pluginPath].should.equal('plugin');
            });

            it('should throw an error for an invalid path', function() {
                var script = avisynth.Script();
                var directory = fakePluginsDir;
                script.load.bind(script,  textFile).should.throw(AvisynthError);
                script.load.bind(script,   missing).should.throw(AvisynthError);
                script.load.bind(script, directory).should.throw(AvisynthError);
            });

            it('should not throw errors if told to ignore them', function() {
                var script = avisynth.Script();
                var directory = fakePluginsDir;
                script.load.bind(script,  textFile, true).should.not.throw();
                script.load.bind(script,   missing, true).should.not.throw();
                script.load.bind(script, directory, true).should.not.throw();
            });

            it('should throw error if the path contains an invalid character', function() {
                var script = avisynth.Script();
                var invalid = path.resolve(__dirname, 'zettai-ryōiki/invalid-path.avs');
                script.load.bind(avisynth, invalid).should.throw(AvisynthError);
            });
        });

        describe('.autoload', function() {
            it('should load plugins and scripts from a given directory', function() {
                var script = avisynth.Script();
                script.autoload(fakePluginsDir);
                script.references[scriptPath].should.equal('script');
                script.references[pluginPath].should.equal('plugin');
            });

            it('should throw an error if the directory does not exist', function() {
                var script = avisynth.Script();
                var invalid = path.resolve(__dirname, 'non-existant');
                script.autoload.bind(script, invalid).should.throw(AvisynthError);
            });

            it('should throw error if the path contains an invalid character', function() {
                var script = avisynth.Script();
                var invalid = path.resolve(__dirname, 'zettai-ryōiki');
                script.autoload.bind(avisynth, invalid).should.throw(AvisynthError);
            });
        });

        describe('.allReferences', function() {
            it('should initially return the global references', function() {
                avisynth.Script().allReferences().should.deep.equal(baseRefs);
            });

            it('should be updated when loading or autoloading', function() {
                var script = avisynth.Script();
                expect(script.allReferences()[scriptPath]).to.be.undefined;
                script.load(scriptPath);
                script.allReferences()[scriptPath].should.equal('script');
                expect(script.allReferences()[pluginPath]).to.be.undefined;
                script.load(pluginPath);
                script.allReferences()[pluginPath].should.equal('plugin');
                script = new avisynth.Script;
                expect(script.allReferences()[scriptPath]).to.be.undefined;
                expect(script.allReferences()[pluginPath]).to.be.undefined;
                script.autoload(fakePluginsDir);
                avisynth.Script().allReferences().should.deep.equal(baseRefs);
                script.allReferences()[scriptPath].should.equal('script');
                script.allReferences()[pluginPath].should.equal('plugin');
            });
        });

        describe('.fullCode', function() {
            it('should be a function that returns a string', function() {
                avisynth.Script().fullCode.should.be.a('function');
                avisynth.Script().fullCode().should.be.a('string');
            });

            it('should embed code to load all initial plugins/scripts', function() {
                var expected = '';
                Object.keys(baseRefs).forEach(function(v) {
                    expected += 'LoadPlugin("' + v + '")\n';
                });
                avisynth.Script().fullCode().should.equal(expected);
            });

            it('should also embed code to load further plugins/scripts', function() {
                var script = avisynth.Script();
                script.load(scriptPath);
                script.load(pluginPath);
                var expected = '';
                Object.keys(baseRefs).forEach(function(v) {
                    expected += 'LoadPlugin("' + v + '")\n';
                });
                expected += 'Import("' + scriptPath + '")\n';
                expected += 'LoadPlugin("' + pluginPath + '")\n';
                script.fullCode().should.equal(expected);
            });

            it('should moreover include any code passed to the construtor', function() {
                var code = 'Version()\nSubtitle("' + rand + '")';
                var script = avisynth.Script(code);
                script.load(scriptPath);
                script.load(pluginPath);
                var expected = '';
                Object.keys(baseRefs).forEach(function(v) {
                    expected += 'LoadPlugin("' + v + '")\n';
                });
                expected += 'Import("' + scriptPath + '")\n';
                expected += 'LoadPlugin("' + pluginPath + '")\n';
                expected += code + '\n';
                script.fullCode().should.equal(expected);
            });
        });

        describe('.code', function() {
            it('should be a function that allows adding code', function() {
                var code = '\nVersion()\n\nSubtitle("'+rand+'")\nSharpen(1)';
                var lines = code.split('\n');
                var script = new avisynth.Script(code);
                lines.forEach(function(line) {
                    script.code(line);
                });
                script.code(code);
                script.rawCode.should.equal(code + '\n' + code + '\n' + code + '\n');
                script.rawCode.should.equal([code, code, code, ''].join('\n'));
            });
        });

        describe('.getPath', function() {
            it('should create and keep a script file', function() {
                var script = avisynth.Script('ColorBars()');
                var base = os.tmpdir() + '/avisynth.js-' + process.pid + '/scripts/';
                var location = base + script.md5() + '.avs';
                fs.existsSync(location).should.be.false;
                script.getPath();
                fs.existsSync(location).should.be.true;
                expect(location).to.equal(script.getPath());
            });
        });

        describe('.renderFrame', function() {
            it('should allow rendering a script to a file', function(done) {
                this.timeout(10000); // Take your time.
                var script = new avisynth.Script('ColorBarsHD()');
                // We're using BMP as a format because it can be consistently hashed.
                var bmp = os.tmpdir() + '/avisynth-test ' + rand + '.bmp';
                var expected = '6be6eacc299e3ee146aeb016c33970b7'; // MD5
                script.renderFrame(bmp, function(err) {
                    if (err) {
                        done(err);
                    } else if (fs.existsSync(bmp)) {
                        var bytes = fs.readFileSync(bmp);
                        var actual = crypto.createHash('md5').update(bytes).digest('hex');
                        fs.unlinkSync(bmp);
                        if (actual !== expected) {
                            done(new Error('md5 mismatch: ' + expected + ' vs ' + actual));
                        } else {
                            done(err);
                        }
                    } else {
                        done(new Error('file not created'));
                    }
                });
            });

            it('should allow rendering specific frames', function(done) {
                this.timeout(10000); // Take your time.
                var script = new avisynth.Script('ColorBarsHD()');
                // Animating so that a particular frame can be identified as such.
                // ShowFrameNumber is problematic because then we rely on precise
                // font rendering, and that's not reliably hashable in this case.
                script.animate(0, 100, 'Levels',
                               0, 1, 255,   0, 255,
                               0, 1, 255, 255, 255);
                var bmp = os.tmpdir() + '/avisynth-test ' + rand + '.bmp';
                var expected = '8c3e13b37151b371f99f354408356a71';
                script.renderFrame(2.5, bmp, function(err) {
                    if (err) {
                        done(err);
                    } else if (fs.existsSync(bmp)) {
                        var bytes = fs.readFileSync(bmp);
                        var actual = crypto.createHash('md5').update(bytes).digest('hex');
                        fs.unlinkSync(bmp);
                        if (actual !== expected) {
                            done(new Error('md5 mismatch: ' + expected + ' vs ' + actual));
                        } else {
                            done(err);
                        }
                    } else {
                        done(new Error('file not created'));
                    }
                });
            });
        });
    });
});
<commit_msg>Test for callback-less condition.<commit_after>var path          = require('path');
var fs            = require('fs');
var os            = require('os');
var crypto        = require('crypto');
var should        = require('chai').should();
var expect        = require('chai').expect;
var avisynth      = require('../main');
var Script        = require('../code/script');
var loader        = require('../code/loader');
var pluginSystem  = require('../code/plugins');
var AvisynthError = require('../code/errors').AvisynthError;

describe('avisynth.Script', function() {
    var baseRefs = loader.references;
    var fakePluginsDir = path.resolve(__dirname, 'plugins');
    var scriptPath = path.resolve(fakePluginsDir, 'colors_rgb.avsi');
    var pluginPath = path.resolve(fakePluginsDir, 'DeDup.dll');
    var textFile   = path.resolve(fakePluginsDir, 'colors_rgb.txt');
    var missing    = path.resolve(fakePluginsDir, 'non-existent.dll');
    var rand = Math.random(); // Guess what, initializing it takes ~260ms for me on Win7.

    it('should be a Script construtor', function() {
        avisynth.Script.should.be.a('function');
        (new avisynth.Script).should.be.instanceof(Script);
    });

    it('should accept omitting the "new" operator', function() {
        avisynth.Script().should.be.instanceof(Script);
    });

    describe('instances', function() {
        it('should accept a code parameter, and include it as a property', function() {
            var code = '\nVersion()\n\nSubtitle("' + rand + '")';
            avisynth.Script(code).rawCode.should.equal(code);
        });

        it('should have empty code if no code was provided', function() {
            avisynth.Script().rawCode.should.equal('');
        });

        it('should integrate with the plugins by prototypal inheritance', function() {
            var script = new avisynth.Script();
            Object.getPrototypeOf(script).should.equal(pluginSystem.pluginPrototype);
        });

        describe('.load', function() {
            it('should load scripts and plugins from given paths', function() {
                var script = avisynth.Script();
                script.load(scriptPath);
                script.load(pluginPath);
                script.references[scriptPath].should.equal('script');
                script.references[pluginPath].should.equal('plugin');
            });

            it('should throw an error for an invalid path', function() {
                var script = avisynth.Script();
                var directory = fakePluginsDir;
                script.load.bind(script,  textFile).should.throw(AvisynthError);
                script.load.bind(script,   missing).should.throw(AvisynthError);
                script.load.bind(script, directory).should.throw(AvisynthError);
            });

            it('should not throw errors if told to ignore them', function() {
                var script = avisynth.Script();
                var directory = fakePluginsDir;
                script.load.bind(script,  textFile, true).should.not.throw();
                script.load.bind(script,   missing, true).should.not.throw();
                script.load.bind(script, directory, true).should.not.throw();
            });

            it('should throw error if the path contains an invalid character', function() {
                var script = avisynth.Script();
                var invalid = path.resolve(__dirname, 'zettai-ryōiki/invalid-path.avs');
                script.load.bind(avisynth, invalid).should.throw(AvisynthError);
            });
        });

        describe('.autoload', function() {
            it('should load plugins and scripts from a given directory', function() {
                var script = avisynth.Script();
                script.autoload(fakePluginsDir);
                script.references[scriptPath].should.equal('script');
                script.references[pluginPath].should.equal('plugin');
            });

            it('should throw an error if the directory does not exist', function() {
                var script = avisynth.Script();
                var invalid = path.resolve(__dirname, 'non-existant');
                script.autoload.bind(script, invalid).should.throw(AvisynthError);
            });

            it('should throw error if the path contains an invalid character', function() {
                var script = avisynth.Script();
                var invalid = path.resolve(__dirname, 'zettai-ryōiki');
                script.autoload.bind(avisynth, invalid).should.throw(AvisynthError);
            });
        });

        describe('.allReferences', function() {
            it('should initially return the global references', function() {
                avisynth.Script().allReferences().should.deep.equal(baseRefs);
            });

            it('should be updated when loading or autoloading', function() {
                var script = avisynth.Script();
                expect(script.allReferences()[scriptPath]).to.be.undefined;
                script.load(scriptPath);
                script.allReferences()[scriptPath].should.equal('script');
                expect(script.allReferences()[pluginPath]).to.be.undefined;
                script.load(pluginPath);
                script.allReferences()[pluginPath].should.equal('plugin');
                script = new avisynth.Script;
                expect(script.allReferences()[scriptPath]).to.be.undefined;
                expect(script.allReferences()[pluginPath]).to.be.undefined;
                script.autoload(fakePluginsDir);
                avisynth.Script().allReferences().should.deep.equal(baseRefs);
                script.allReferences()[scriptPath].should.equal('script');
                script.allReferences()[pluginPath].should.equal('plugin');
            });
        });

        describe('.fullCode', function() {
            it('should be a function that returns a string', function() {
                avisynth.Script().fullCode.should.be.a('function');
                avisynth.Script().fullCode().should.be.a('string');
            });

            it('should embed code to load all initial plugins/scripts', function() {
                var expected = '';
                Object.keys(baseRefs).forEach(function(v) {
                    expected += 'LoadPlugin("' + v + '")\n';
                });
                avisynth.Script().fullCode().should.equal(expected);
            });

            it('should also embed code to load further plugins/scripts', function() {
                var script = avisynth.Script();
                script.load(scriptPath);
                script.load(pluginPath);
                var expected = '';
                Object.keys(baseRefs).forEach(function(v) {
                    expected += 'LoadPlugin("' + v + '")\n';
                });
                expected += 'Import("' + scriptPath + '")\n';
                expected += 'LoadPlugin("' + pluginPath + '")\n';
                script.fullCode().should.equal(expected);
            });

            it('should moreover include any code passed to the construtor', function() {
                var code = 'Version()\nSubtitle("' + rand + '")';
                var script = avisynth.Script(code);
                script.load(scriptPath);
                script.load(pluginPath);
                var expected = '';
                Object.keys(baseRefs).forEach(function(v) {
                    expected += 'LoadPlugin("' + v + '")\n';
                });
                expected += 'Import("' + scriptPath + '")\n';
                expected += 'LoadPlugin("' + pluginPath + '")\n';
                expected += code + '\n';
                script.fullCode().should.equal(expected);
            });
        });

        describe('.code', function() {
            it('should be a function that allows adding code', function() {
                var code = '\nVersion()\n\nSubtitle("'+rand+'")\nSharpen(1)';
                var lines = code.split('\n');
                var script = new avisynth.Script(code);
                lines.forEach(function(line) {
                    script.code(line);
                });
                script.code(code);
                script.rawCode.should.equal(code + '\n' + code + '\n' + code + '\n');
                script.rawCode.should.equal([code, code, code, ''].join('\n'));
            });
        });

        describe('.getPath', function() {
            it('should create and keep a script file', function() {
                var script = avisynth.Script('ColorBars()');
                var base = os.tmpdir() + '/avisynth.js-' + process.pid + '/scripts/';
                var location = base + script.md5() + '.avs';
                fs.existsSync(location).should.be.false;
                script.getPath();
                fs.existsSync(location).should.be.true;
                expect(location).to.equal(script.getPath());
            });
        });

        describe('.renderFrame', function() {
            it('should allow rendering a script to a file', function(done) {
                this.timeout(10000); // Take your time.
                var script = new avisynth.Script('ColorBarsHD()');
                // We're using BMP as a format because it can be consistently hashed.
                var bmp = os.tmpdir() + '/avisynth-test ' + rand + '.bmp';
                var expected = '6be6eacc299e3ee146aeb016c33970b7'; // MD5
                script.renderFrame(bmp, function(err) {
                    if (err) {
                        done(err);
                    } else if (fs.existsSync(bmp)) {
                        var bytes = fs.readFileSync(bmp);
                        var actual = crypto.createHash('md5').update(bytes).digest('hex');
                        fs.unlinkSync(bmp);
                        if (actual !== expected) {
                            done(new Error('md5 mismatch: ' + expected + ' vs ' + actual));
                        } else {
                            done(err);
                        }
                    } else {
                        done(new Error('file not created'));
                    }
                });
            });

            it('should allow rendering specific frames', function(done) {
                this.timeout(10000); // Take your time.
                var script = new avisynth.Script('ColorBarsHD()');
                // Animating so that a particular frame can be identified as such.
                // ShowFrameNumber is problematic because then we rely on precise
                // font rendering, and that's not reliably hashable in this case.
                script.animate(0, 100, 'Levels',
                               0, 1, 255,   0, 255,
                               0, 1, 255, 255, 255);
                var bmp = os.tmpdir() + '/avisynth-test ' + rand + '.bmp';
                var expected = '8c3e13b37151b371f99f354408356a71';
                script.renderFrame(2.5, bmp, function(err) {
                    if (err) {
                        done(err);
                    } else if (fs.existsSync(bmp)) {
                        var bytes = fs.readFileSync(bmp);
                        var actual = crypto.createHash('md5').update(bytes).digest('hex');
                        fs.unlinkSync(bmp);
                        if (actual !== expected) {
                            done(new Error('md5 mismatch: ' + expected + ' vs ' + actual));
                        } else {
                            done(err);
                        }
                    } else {
                        done(new Error('file not created'));
                    }
                });
            });

            it('callback is optional', function(done) {
                this.timeout(3100); // This is scheduled.
                var script = new avisynth.Script('ColorBarsHD()');
                var bmp = os.tmpdir() + '/avisynth-test-2 ' + rand + '.bmp';
                script.renderFrame(bmp);
                setTimeout(done, 3000);
            });
        });
    });
});
<|endoftext|>


<commit_before>/**
 *  Sync
 *  ====
 *
 *  A synchronized shared object - which accessible by all users.
 */

/**
 *  Public interface for the synchronize object
 *
 *  @type {[type]}
 */

SYNC = getReactor( batch(sync) );

var CACHE  = {},  // record of still pending properties

    SOLVED = {};  // temporary list for confirmed values

/**
 *  Combine multiplee changes to one batch,
 *  to process them as one and avoid unrequired network transfer
 *
 *  @param  {Function} fn    -
 *  @return {Function}
 */

function batch ( fn ) {

  var list      = {},

      timeoutID = null;

  function share()  {

    timeoutID = null;

    var keys = getKeys(list),

        prop;

    for ( var i = 0, l = keys.length; i < l; i++ ) {

      prop = keys[ i ];

      sync( prop, list[ prop ] );

      delete list[ prop ];
    }

    /** defined in game -> forward **/
    if ( STARTER ) STARTER();
  }

  return function ( key, value ) {

    list[key] = value;

    if ( timeoutID ) clearTimeout( timeoutID );

    timeoutID = setTimeout( share, DELAY );
  };
}

/**
 *  Share the property (key/value) with other peers
 *
 *  @param  {String}               key         -
 *  @param  {String|Number|Object} value       -
 *  @param  {Boolean}              confirmed   -
 */

function sync ( key, value, confirmed ) {

  if ( confirmed ) {

    if ( !CACHE[key] || CACHE[key].results[ PLAYER.pos ] !== value ) {

      SOLVED[ key ]  = true;
      pg.sync[ key ] = value;
    }

    delete CACHE[key];        // console.log( '[CONFIRMED]', value    );
    return;
  }

  if ( CACHE[ key ] ) return; // console.log( '[CACHED]', CACHE[key]  );

  if ( SOLVED[ key ] ) {      // console.log( '[SOLVED]', SOLVED[key] );

    delete SOLVED[key];
    return;
  }

  var ids = getKeys( CONNECTIONS );

  // TODO: 0.6.0 -> conflict with multiple ?
  CACHE[key] = { list: ids, results: [] };

  CACHE[key].results[ PLAYER.pos ] = value;

  for ( var i = 0, l = ids.length; i < l; i++ ) {

    CONNECTIONS[ ids[i] ].send( 'sync', { key: key, value: value }, true );
  }
}

/**
 *  Exchange value with remote data & merge on conflict
 *
 *  @param  {String} remoteID   -
 *  @param  {String} key        -
 *  @param  {String} value      -
 */

function resync ( remoteID, key, value ) {

  if ( !CACHE[key] ) { // noConflict

    sync( key, value, true );

    return CONNECTIONS[ remoteID ].send( 'sync', { resync: true, key: key, value: value }, true );
  }

  // TODO: 0.6.0 -> handle conflict (lower pos)
  console.log('[CONFLICT]');

  // var entry = CACHE[key];

  // entry.list.length -= 1;

  // entry.results[ PEERS[remoteID].pos ] = value;

  // if ( entry.list.length ) return;
}
<commit_msg>solve sync conflicts<commit_after>/**
 *  Sync
 *  ====
 *
 *  A synchronized shared object - which accessible by all users.
 */

/**
 *  Public interface for the synchronize object
 *
 *  @type {[type]}
 */

SYNC = getReactor( batch(sync) );

var CACHE    = {},  // record of still pending properties

    SOLVED   = {},  // temporary list for confirmed values

    // SYNCNO   =  0,  // version tracking , 0.8

    SYNCFLOW = {    // steps for synchronisation

      'request' : requestSync,
      'confirm' : confirmSync
    };

/**
 *  Combine multiple changes to one batch,
 *  to process them as one and avoid unrequired network transfer,
 *  especially as complex objects or arrays can be transfered and
 *  else each property would be synced with one transmission !
 *
 *  @param  {Function} fn    -
 *  @return {Function}
 */

function batch ( fn ) {

  var list      = {},

      timeoutID = null;

  function share()  {

    timeoutID = null;

    var keys = getKeys(list),

        prop;

    for ( var i = 0, l = keys.length; i < l; i++ ) {

      prop = keys[ i ];

      sync( prop, list[ prop ] );

      delete list[ prop ];
    }

  }

  return function ( key, value ) {

    list[key] = value;

    if ( timeoutID ) clearTimeout( timeoutID );

    timeoutID = setTimeout( share, DELAY );
  };
}

/**
 *  Share the property (key/value) with other peers
 *
 *  @param  {String}               key         -
 *  @param  {String|Number|Object} value       -
 *  @param  {Boolean}              confirmed   -
 */

function sync ( key, value, resync ) {

  var resolved = !handleCaches.apply( this, arguments );

  if ( resolved ) return;

  var ids = getKeys( CONNECTIONS );

  CACHE[key] = { list: ids, results: [] };

  CACHE[key].results[ PLAYER.pos ] = value;

  MANAGER.broadcast( 'sync', { action: 'request', key: key, value: value }, true );

  // SYNCNO++;   // 0.8 -> improve conflict solving, using versioning for the caches & sync
  // MANAGER.broadcast( 'sync', { version: SYNCNO, action: 'request', key: key, value: value }, true );
}

/**
 *
 *
 *  @param  {String} remoteID   -
 *  @param  {String} key        -
 *  @param  {String} value      -
 */

function requestSync ( remoteID, key, value ) {

  var entry = CACHE[key];

  if ( entry != void 0 ) value = entry;

  CONNECTIONS[ remoteID ].send( 'sync', { action: 'confirm', key: key, value: value }, true );
}

/**
 *  Exchange value with remote data & merge on conflict / merge....
 */

function confirmSync ( remoteID, key, value ) { // resync to all

  var entry = CACHE[key];

  entry.results[ PEERS[remoteID].pos ] = value;

  entry.list.length--;

  // TODO: requires responses from all requests or deadlock
  if ( entry.list.length > 0 ) return;

  value = entry.results[0]; // priority

  sync( key, value, true ); // set local

  MANAGER.broadcast( 'sync', { key: key, value: value }, true );
}

/**
 *  [handleCaches description]
 *
 *  // returns true if it shouldn't be shared/sent to remote peers
 *  // remote setting their local value
 *
 *  @param  {[type]} key    [description]
 *  @param  {[type]} value  [description]
 *  @param  {[type]} resync [description]
 *  @return {[type]}        [description]
 */

function handleCaches ( key, value, resync ) {

  if ( resync ) {

    if ( !CACHE[key] ) {

      SOLVED[  key ] = true;

      pg.sync[ key ] = value;
    }

    if ( CACHE[key] ) delete CACHE[key];

    return;                                             // console.log( '[CONFIRMED]', value    );
  }

  if ( CACHE[key] ) return;                             // console.log( '[CACHED]', CACHE[key]  );

  if ( SOLVED[ key ] ) { delete SOLVED[key]; return; }  // console.log( '[SOLVED]', SOLVED[key] );

  return true;
}

/**
 *  Check the status of the caches, e.g. they are ready (empty)
 */

function checkCaches(){

  return getKeys(CACHE).length || getKeys(SOLVED).length;
}
<|endoftext|>


<commit_before>/*
* @author Niklas von Hertzen <niklas at hertzen.com>
* @created 3.3.2012
* @website http://hertzen.com
 */

module("CSS");
$(function() {

    var propsToTest = ["borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth"],
    numDivs = $('#borders div').length;
    /*
    expecting = [
    // #1
    "1px",
    "0px",
    "1px",
    "0px",

    // #2
    "16px",
    "0px",
    "16px",
    "0px",

    // #3
    "1px",
    "3px",
    "5px",
    "3px",

    // #4
    "3px",
    "3px",
    "3px",
    "3px",

    // #5
    "80px",
    "35px",
    "480px",
    "188px",

    // #6
    "3904px",
    "3500px",
    "2944px",
    "2513px",

    // #7
    "18px",
    "6px",
    "80px",
    "5px",

    // #8
    "1889px",
    "666px",
    "3904px",
    "500px"
    ];
       */

    test('border-width', propsToTest.length * numDivs, function() {

        $('#borders div').each(function(i, el) {
            $.each(propsToTest, function(s, prop) {
                var expect = $(el).css(prop);

                // older IE's don't necessarily return px even with jQuery
                if (expect === "thin") {
                    expect = "1px";
                } else if (expect === "medium") {
                    expect = "3px";
                } else if (expect === "thick") {
                    expect = "5px";
                }

                QUnit.equal( _html2canvas.Util.getCSS(el, prop), expect, "div #" + (i + 1) + " property " + prop + " equals " + $(el).css(prop) );
            });

        });
    });

    var propsToTest2 = ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft"],
    numDivs2 = $('#padding div').length;

    test('padding width', propsToTest2.length * numDivs2, function() {

        $('#padding div').each(function(i, el) {
            $.each(propsToTest2, function(s, prop) {
                QUnit.equal( _html2canvas.Util.getCSS(el, prop), $(el).css(prop), "div #" + (i + 1) + " property " + prop + " equals " + $(el).css(prop) );
            });

        });
    });

    var propsToTest3 = ["backgroundPosition"],
    numDivs3 = $('#backgroundPosition div').length;

    test('background-position', propsToTest3.length * numDivs3 * 2, function() {

        $('#backgroundPosition div').each(function(i, el) {
            $.each(propsToTest3, function(s, prop) {
                var img =  new Image();
                img.width = 50;
                img.height = 50;
                var item = _html2canvas.Util.getCSS(el, prop),
                pos = _html2canvas.Util.BackgroundPosition(el, _html2canvas.Util.Bounds(el), img);

                var split = $(el).css(prop).split(" ");
                var testEl = $('<div />').css({
                    'position': 'absolute',
                    'left': split[0],
                    'top': split[1]
                });

                testEl.appendTo(el);

                QUnit.equal( pos.left, parseFloat(testEl.css('left'), 10), "div #" + (i + 1) + " background-position-x equals " + pos.left + " from " + item );
                QUnit.equal( pos.top, parseFloat(testEl.css('top'), 10), "div #" + (i + 1) + " background-position-y equals " + pos.top );

                testEl.remove();

            });

        });
    });

    // TODO add backgroundPosition % tests
});<commit_msg>padding width needs to be in pixels<commit_after>/*
* @author Niklas von Hertzen <niklas at hertzen.com>
* @created 3.3.2012
* @website http://hertzen.com
 */

module("CSS");
$(function() {

    var propsToTest = ["borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth"],
    numDivs = $('#borders div').length;
    /*
    expecting = [
    // #1
    "1px",
    "0px",
    "1px",
    "0px",

    // #2
    "16px",
    "0px",
    "16px",
    "0px",

    // #3
    "1px",
    "3px",
    "5px",
    "3px",

    // #4
    "3px",
    "3px",
    "3px",
    "3px",

    // #5
    "80px",
    "35px",
    "480px",
    "188px",

    // #6
    "3904px",
    "3500px",
    "2944px",
    "2513px",

    // #7
    "18px",
    "6px",
    "80px",
    "5px",

    // #8
    "1889px",
    "666px",
    "3904px",
    "500px"
    ];
       */

    test('border-width', propsToTest.length * numDivs, function() {

        $('#borders div').each(function(i, el) {
            $.each(propsToTest, function(s, prop) {
                var expect = $(el).css(prop);

                // older IE's don't necessarily return px even with jQuery
                if (expect === "thin") {
                    expect = "1px";
                } else if (expect === "medium") {
                    expect = "3px";
                } else if (expect === "thick") {
                    expect = "5px";
                }
                QUnit.equal( _html2canvas.Util.getCSS(el, prop), expect, "div #" + (i + 1) + " property " + prop + " equals " + $(el).css(prop) );
            });

        });
    });

    var propsToTest2 = ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft"],
    numDivs2 = $('#padding div').length;

    test('padding width', propsToTest2.length * numDivs2 * 2, function() {

        $('#padding div').each(function(i, el) {
            $.each(propsToTest2, function(s, prop) {
                var isPx = _html2canvas.Util.getCSS(el, prop).indexOf("px");
                QUnit.notEqual( isPx, -1, "div #" + (i + 1) + " property " + prop + " is in pixels" );
                QUnit.equal( _html2canvas.Util.getCSS(el, prop), $(el).css(prop), "div #" + (i + 1) + " property " + prop + " equals " + $(el).css(prop) );
            });

        });
    });

    var propsToTest3 = ["backgroundPosition"],
    numDivs3 = $('#backgroundPosition div').length;

    test('background-position', propsToTest3.length * numDivs3 * 2, function() {

        $('#backgroundPosition div').each(function(i, el) {
            $.each(propsToTest3, function(s, prop) {
                var img =  new Image();
                img.width = 50;
                img.height = 50;
                var item = _html2canvas.Util.getCSS(el, prop),
                pos = _html2canvas.Util.BackgroundPosition(el, _html2canvas.Util.Bounds(el), img);

                var split = $(el).css(prop).split(" ");
                var testEl = $('<div />').css({
                    'position': 'absolute',
                    'left': split[0],
                    'top': split[1]
                });

                testEl.appendTo(el);

                QUnit.equal( pos.left, parseFloat(testEl.css('left'), 10), "div #" + (i + 1) + " background-position-x equals " + pos.left + " from " + item );
                QUnit.equal( pos.top, parseFloat(testEl.css('top'), 10), "div #" + (i + 1) + " background-position-y equals " + pos.top );

                testEl.remove();

            });

        });
    });

// TODO add backgroundPosition % tests
});<|endoftext|>