"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var android_device_discovery_1 = require("../../../mobile/mobile-core/android-device-discovery");
var yok_1 = require("../../../yok");
var Future = require("fibers/future");
var events_1 = require("events");
var os_1 = require("os");
var chai_1 = require("chai");
var AndroidDeviceMock = (function () {
    function AndroidDeviceMock(identifier, status) {
        this.identifier = identifier;
        this.status = status;
        this.deviceInfo = {};
        this.deviceInfo.identifier = identifier;
    }
    return AndroidDeviceMock;
})();
;
;
var MockEventEmitter = (function (_super) {
    __extends(MockEventEmitter, _super);
    function MockEventEmitter() {
        _super.apply(this, arguments);
    }
    return MockEventEmitter;
})(events_1.EventEmitter);
;
var mockStdoutEmitter, mockStderrEmitter, mockChildProcess;
function createTestInjector() {
    var injector = new yok_1.Yok();
    injector.register("injector", injector);
    injector.register("childProcess", {
        spawn: function (command, args, options) {
            mockChildProcess = new MockEventEmitter();
            mockChildProcess.stdout = mockStdoutEmitter;
            mockChildProcess.stderr = mockStderrEmitter;
            return mockChildProcess;
        },
        spawnFromEvent: function (command, args, event, options, spawnFromEventOptions) {
            return Future.fromResult(args);
        }
    });
    injector.register("staticConfig", {
        getAdbFilePath: function () {
            return Future.fromResult("adbPath");
        }
    });
    injector.register("androidDeviceDiscovery", android_device_discovery_1.AndroidDeviceDiscovery);
    var originalResolve = injector.resolve;
    var resolve = function (param, ctorArguments) {
        if (ctorArguments && Object.prototype.hasOwnProperty.call(ctorArguments, "status") &&
            Object.prototype.hasOwnProperty.call(ctorArguments, "identifier")) {
            return new AndroidDeviceMock(ctorArguments["identifier"], ctorArguments["status"]);
        }
        else {
            return originalResolve.apply(injector, [param, ctorArguments]);
        }
    };
    injector.resolve = resolve;
    return injector;
}
describe("androidDeviceDiscovery", function () {
    var androidDeviceDiscovery, injector, androidDeviceIdentifier = "androidDevice", androidDeviceStatus = "device", devicesFound;
    beforeEach(function () {
        mockChildProcess = null;
        injector = createTestInjector();
        mockStdoutEmitter = new MockEventEmitter();
        mockStderrEmitter = new MockEventEmitter();
        androidDeviceDiscovery = injector.resolve("androidDeviceDiscovery");
        devicesFound = [];
    });
    describe("startLookingForDevices", function () {
        it("finds correctly one device", function () {
            androidDeviceDiscovery.on("deviceFound", function (device) {
                devicesFound.push(device);
            });
            setTimeout(function () {
                var output = "List of devices attached " + os_1.EOL + androidDeviceIdentifier + "\t" + androidDeviceStatus + os_1.EOL + os_1.EOL;
                mockStdoutEmitter.emit('data', output);
                mockChildProcess.emit('close', 0);
            }, 1);
            androidDeviceDiscovery.startLookingForDevices().wait();
            chai_1.assert.isTrue(devicesFound.length === 1, "We should have found ONE device.");
            chai_1.assert.deepEqual(devicesFound[0].deviceInfo.identifier, androidDeviceIdentifier);
            chai_1.assert.deepEqual(devicesFound[0].status, androidDeviceStatus);
        });
    });
    describe("ensureAdbServerStarted", function () {
        it("should spawn adb with start-server parameter", function () {
            var ensureAdbServerStartedOutput = androidDeviceDiscovery.ensureAdbServerStarted().wait();
            chai_1.assert.isTrue(_.contains(ensureAdbServerStartedOutput, "start-server"), "start-server should be passed to adb.");
        });
    });
    describe("checkForDevices", function () {
        it("finds correctly one device", function () {
            var future = new Future();
            androidDeviceDiscovery.on("deviceFound", function (device) {
                devicesFound.push(device);
                future.return();
            });
            androidDeviceDiscovery.checkForDevices().wait();
            var output = "List of devices attached " + os_1.EOL + androidDeviceIdentifier + "\t" + androidDeviceStatus + os_1.EOL + os_1.EOL;
            mockStdoutEmitter.emit('data', output);
            mockChildProcess.emit('close', 0);
            future.wait();
            chai_1.assert.isTrue(devicesFound.length === 1, "We should have found ONE device.");
            chai_1.assert.deepEqual(devicesFound[0].deviceInfo.identifier, androidDeviceIdentifier);
            chai_1.assert.deepEqual(devicesFound[0].status, androidDeviceStatus);
        });
        it("finds correctly more than one device", function () {
            var future = new Future();
            androidDeviceDiscovery.on("deviceFound", function (device) {
                devicesFound.push(device);
                if (devicesFound.length === 2) {
                    future.return();
                }
            });
            androidDeviceDiscovery.checkForDevices().wait();
            var output = "List of devices attached " + os_1.EOL + androidDeviceIdentifier + "\t" + androidDeviceStatus + os_1.EOL + "secondDevice\t" + androidDeviceStatus + os_1.EOL;
            mockStdoutEmitter.emit('data', output);
            mockChildProcess.emit('close', 0);
            future.wait();
            chai_1.assert.isTrue(devicesFound.length === 2, "We should have found two devices.");
            chai_1.assert.deepEqual(devicesFound[0].deviceInfo.identifier, androidDeviceIdentifier);
            chai_1.assert.deepEqual(devicesFound[0].status, androidDeviceStatus);
            chai_1.assert.deepEqual(devicesFound[1].deviceInfo.identifier, "secondDevice");
            chai_1.assert.deepEqual(devicesFound[1].status, androidDeviceStatus);
        });
        it("does not find any devices when there are no devices", function () {
            androidDeviceDiscovery.on("deviceFound", function (device) {
                throw new Error("Devices should not be found.");
            });
            androidDeviceDiscovery.checkForDevices().wait();
            var output = "List of devices attached" + os_1.EOL;
            mockStdoutEmitter.emit('data', output);
            mockChildProcess.emit('close', 0);
            chai_1.assert.isTrue(devicesFound.length === 0, "We should have NOT found devices.");
        });
        describe("when device is already found", function () {
            var defaultAdbOutput = "List of devices attached " + os_1.EOL + androidDeviceIdentifier + "\t" + androidDeviceStatus + os_1.EOL + os_1.EOL;
            beforeEach(function () {
                var future = new Future();
                var deviceFoundHandler = function (device) {
                    devicesFound.push(device);
                    future.return();
                };
                androidDeviceDiscovery.on("deviceFound", deviceFoundHandler);
                androidDeviceDiscovery.checkForDevices().wait();
                mockStdoutEmitter.emit('data', defaultAdbOutput);
                mockChildProcess.emit('close', 0);
                future.wait();
                androidDeviceDiscovery.removeListener("deviceFound", deviceFoundHandler);
            });
            it("does not report it as found next time when checkForDevices is called and same device is still connected", function () {
                androidDeviceDiscovery.on("deviceFound", function (device) {
                    throw new Error("Should not report same device as found");
                });
                androidDeviceDiscovery.checkForDevices().wait();
                mockStdoutEmitter.emit('data', defaultAdbOutput);
                mockChildProcess.emit('close', 0);
                chai_1.assert.isTrue(devicesFound.length === 1, "We should have found ONE device.");
                chai_1.assert.deepEqual(devicesFound[0].deviceInfo.identifier, androidDeviceIdentifier);
                chai_1.assert.deepEqual(devicesFound[0].status, androidDeviceStatus);
            });
            it("reports it as removed next time when called and device is removed", function () {
                var future = new Future();
                androidDeviceDiscovery.on("deviceLost", function (device) {
                    future.return(device);
                });
                androidDeviceDiscovery.checkForDevices().wait();
                var output = "List of devices attached" + os_1.EOL;
                mockStdoutEmitter.emit('data', output);
                mockChildProcess.emit('close', 0);
                var lostDevice = future.wait();
                chai_1.assert.deepEqual(lostDevice.deviceInfo.identifier, androidDeviceIdentifier);
                chai_1.assert.deepEqual(lostDevice.status, androidDeviceStatus);
            });
            it("does not report it as removed two times when called and device is removed", function () {
                var future = new Future();
                androidDeviceDiscovery.on("deviceLost", function (device) {
                    future.return(device);
                });
                androidDeviceDiscovery.checkForDevices().wait();
                var output = "List of devices attached" + os_1.EOL;
                mockStdoutEmitter.emit('data', output);
                mockChildProcess.emit('close', 0);
                var lostDevice = future.wait();
                chai_1.assert.deepEqual(lostDevice.deviceInfo.identifier, androidDeviceIdentifier);
                chai_1.assert.deepEqual(lostDevice.status, androidDeviceStatus);
                androidDeviceDiscovery.on("deviceLost", function (device) {
                    throw new Error("Should not report device as removed next time after it has been already reported.");
                });
                androidDeviceDiscovery.checkForDevices().wait();
                mockStdoutEmitter.emit('data', output);
                mockChildProcess.emit('close', 0);
            });
            it("reports it as removed and added after that next time when called and device's status is changed", function () {
                var deviceLostFuture = new Future();
                var deviceFoundFuture = new Future();
                androidDeviceDiscovery.on("deviceLost", function (device) {
                    _.remove(devicesFound, function (d) { return d.deviceInfo.identifier === device.deviceInfo.identifier; });
                    deviceLostFuture.return(device);
                });
                androidDeviceDiscovery.on("deviceFound", function (device) {
                    devicesFound.push(device);
                    deviceFoundFuture.return(device);
                });
                androidDeviceDiscovery.checkForDevices().wait();
                var output = "List of devices attached" + os_1.EOL + androidDeviceIdentifier + "\tunauthorized" + os_1.EOL + os_1.EOL;
                mockStdoutEmitter.emit('data', output);
                mockChildProcess.emit('close', 0);
                var lostDevice = deviceLostFuture.wait();
                chai_1.assert.deepEqual(lostDevice.deviceInfo.identifier, androidDeviceIdentifier);
                chai_1.assert.deepEqual(lostDevice.status, androidDeviceStatus);
                deviceFoundFuture.wait();
                chai_1.assert.isTrue(devicesFound.length === 1, "We should have found ONE device.");
                chai_1.assert.deepEqual(devicesFound[0].deviceInfo.identifier, androidDeviceIdentifier);
                chai_1.assert.deepEqual(devicesFound[0].status, "unauthorized");
                androidDeviceDiscovery.checkForDevices().wait();
                mockStdoutEmitter.emit('data', output);
                mockChildProcess.emit('close', 0);
                chai_1.assert.isTrue(devicesFound.length === 1, "We should have found ONE device.");
            });
        });
        it("throws error when adb writes on stderr", function () {
            androidDeviceDiscovery.on("deviceFound", function (device) {
                throw new Error("Devices should not be found.");
            });
            var error = new Error("ADB Error");
            try {
                androidDeviceDiscovery.checkForDevices().wait();
                mockStderrEmitter.emit('data', error);
            }
            catch (err) {
                chai_1.assert.deepEqual(err, error);
            }
        });
        it("throws error when adb's child process throws error", function () {
            androidDeviceDiscovery.on("deviceFound", function (device) {
                throw new Error("Devices should not be found.");
            });
            var error = new Error("ADB Error");
            try {
                androidDeviceDiscovery.checkForDevices().wait();
                mockChildProcess.emit('error', error);
            }
            catch (err) {
                chai_1.assert.deepEqual(err, error);
            }
        });
    });
});
