Added tests
This commit is contained in:
Родитель
a16e0f8903
Коммит
ac4560655f
|
@ -23,10 +23,13 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TypeScriptCompile Include="..\..\src\adapter.ts">
|
||||
<Link>adapter.ts</Link>
|
||||
<Link>src\adapter.ts</Link>
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="..\..\src\cli.ts">
|
||||
<Link>cli.ts</Link>
|
||||
<Link>src\cli.ts</Link>
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="..\..\tests\adapterTests.ts">
|
||||
<Link>tests\adapterTests.ts</Link>
|
||||
</TypeScriptCompile>
|
||||
<TypeScriptCompile Include="..\typings\adapter.d.ts">
|
||||
<Link>typings\adapter.d.ts</Link>
|
||||
|
@ -35,6 +38,11 @@
|
|||
<Link>typings\node.d.ts</Link>
|
||||
</TypeScriptCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\main.js">
|
||||
<Link>main.js</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -10,15 +10,15 @@ var debugHost = "localhost";
|
|||
//var debugHost = "10.137.229.56";
|
||||
|
||||
var PORT = 6767;
|
||||
var MAX_RETRIES = 5;
|
||||
var PROTOCOL_VERSION = 1;
|
||||
|
||||
var sessionFilePath = p.join(os.tmpdir(), "hwa.session");
|
||||
|
||||
module ProxyAdapter {
|
||||
export var _sessionFilePath = p.join(os.tmpdir(), "hwa.session");
|
||||
export var _numRetries = 5;
|
||||
|
||||
export function clearSession() {
|
||||
if (fs.existsSync(sessionFilePath)) {
|
||||
fs.unlink(sessionFilePath);
|
||||
if (fs.existsSync(_sessionFilePath)) {
|
||||
fs.unlinkSync(_sessionFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,20 @@ module ProxyAdapter {
|
|||
|
||||
export function registerAndLaunchAppxManifest(path: string) {
|
||||
// Compress the entire folder where the AppxManifest file is and send it to the VM.
|
||||
rpc("deployAppxManifest", ProxyAdapter._compressPath(p.dirname(p.resolve(path))));
|
||||
}
|
||||
|
||||
export function _launchRdp(address: string) {
|
||||
var rdConfig = fs.readFileSync("./templates/rdConfig.rdp", "utf-8");
|
||||
rdConfig = rdConfig.replace("{address}", address);
|
||||
fs.writeFileSync("./bin/rdConfig.rdp", rdConfig, "utf-8");
|
||||
cp.execSync("start " + "./bin/rdConfig.rdp");
|
||||
}
|
||||
|
||||
export function _compressPath(path: string) {
|
||||
var zip = new admzip();
|
||||
zip.addLocalFolder(p.dirname(p.resolve(path)));
|
||||
rpc("deployAppxManifest", zip.toBuffer());
|
||||
zip.addLocalFolder(path);
|
||||
return zip.toBuffer();
|
||||
}
|
||||
}
|
||||
var typeCheck: HWAProxyAdapter = ProxyAdapter;
|
||||
|
@ -50,14 +61,14 @@ function establishConnection(sessionOnly: boolean, callback: (socket: net.Socket
|
|||
error(e);
|
||||
});
|
||||
socket.connect(6767, address, () => {
|
||||
socket.removeAllListeners("error");
|
||||
socket.removeAllListeners("error");
|
||||
success(socket);
|
||||
});
|
||||
}
|
||||
|
||||
if (fs.existsSync(sessionFilePath)) {
|
||||
if (fs.existsSync(ProxyAdapter._sessionFilePath)) {
|
||||
// Found session file, try reconnecting
|
||||
var sessionFile = fs.readFileSync(sessionFilePath, "utf-8");
|
||||
var sessionFile = fs.readFileSync(ProxyAdapter._sessionFilePath, "utf-8");
|
||||
var address = sessionFile;
|
||||
|
||||
doConnect(address,
|
||||
|
@ -77,16 +88,13 @@ function establishConnection(sessionOnly: boolean, callback: (socket: net.Socket
|
|||
var address = debugHost;
|
||||
|
||||
// Establish Remote Desktop
|
||||
var rdConfig = fs.readFileSync("./templates/rdConfig.rdp", "utf-8");
|
||||
rdConfig = rdConfig.replace("{address}", address);
|
||||
fs.writeFileSync("./bin/rdConfig.rdp", rdConfig, "utf-8");
|
||||
cp.execSync("start " + "./bin/rdConfig.rdp");
|
||||
ProxyAdapter._launchRdp(address);
|
||||
|
||||
// Connect to remote socket
|
||||
var retriesLeft = MAX_RETRIES;
|
||||
var retriesLeft = ProxyAdapter._numRetries;
|
||||
var successHandler = function successHandler(socket: net.Socket) {
|
||||
// Connection successful, save to session file
|
||||
fs.writeFileSync(sessionFilePath, address, "utf-8");
|
||||
fs.writeFileSync(ProxyAdapter._sessionFilePath, address, "utf-8");
|
||||
callback(socket);
|
||||
};
|
||||
var errorHandler = function errorHandler(e: string) {
|
||||
|
@ -125,7 +133,6 @@ function rpc(command: string, data?: Buffer, sessionOnly = false) {
|
|||
socket.destroy();
|
||||
});
|
||||
socket.once("error", (e: any) => {
|
||||
console.log(e);
|
||||
socket.destroy();
|
||||
});
|
||||
socket.write(payloadBuffer);
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
declare function afterEach(teardownFunc: Function): void;
|
||||
declare function beforeEach(setupFunc: Function): void;
|
||||
declare function describe(what: string, body: Function): void;
|
||||
declare function it(should: string, testFunction: (done?: Function) => void): void;
|
||||
declare var mocha: any;
|
||||
|
||||
import assert = require("assert");
|
||||
import cp = require("child_process");
|
||||
import fs = require("fs");
|
||||
import net = require("net");
|
||||
|
||||
import adapter = require("../src/adapter");
|
||||
|
||||
class MockObject {
|
||||
onceHandlers: { eventName: string; callback: Function; }[] = [];
|
||||
onHandlers: { eventName: string; callback: Function; }[] = [];
|
||||
|
||||
on(eventName: string, callback: Function) {
|
||||
this.onHandlers.push({ eventName: eventName, callback: callback });
|
||||
}
|
||||
|
||||
once(eventName: string, callback: Function) {
|
||||
this.onceHandlers.push({ eventName: eventName, callback: callback });
|
||||
}
|
||||
|
||||
removeAllListeners(eventName: string) {
|
||||
for (var i = this.onceHandlers.length - 1; i >= 0; i--) {
|
||||
var handler = this.onceHandlers[i];
|
||||
if (handler.eventName === eventName) {
|
||||
this.onceHandlers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
for (var i = this.onHandlers.length - 1; i >= 0; i--) {
|
||||
var handler = this.onHandlers[i];
|
||||
if (handler.eventName === eventName) {
|
||||
this.onHandlers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_dispatch(eventName: string, eventObj: any) {
|
||||
this.onHandlers.forEach(handler => {
|
||||
if (handler.eventName !== eventName) {
|
||||
return;
|
||||
}
|
||||
handler.callback(eventObj);
|
||||
});
|
||||
|
||||
for (var i = this.onceHandlers.length - 1; i >= 0; i--) {
|
||||
var handler = this.onceHandlers[i];
|
||||
if (handler.eventName !== eventName) {
|
||||
continue;
|
||||
}
|
||||
handler.callback(eventObj);
|
||||
this.onceHandlers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MockSocket extends MockObject {
|
||||
private static _kidnapper: (socket: MockSocket) => void = null;
|
||||
|
||||
destroyed = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (MockSocket._kidnapper) {
|
||||
var kidnapper = MockSocket._kidnapper;
|
||||
MockSocket._kidnapper = null;
|
||||
kidnapper(this);
|
||||
}
|
||||
}
|
||||
|
||||
connect(port: number, host: string, callback: Function) {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
}
|
||||
|
||||
write(buffer: Buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static kidnapNextInstance(kidnapper: (socket: MockSocket) => void) {
|
||||
MockSocket._kidnapper = kidnapper;
|
||||
}
|
||||
}
|
||||
|
||||
class SuccessMockSocket extends MockSocket {
|
||||
connect(port: number, host: string, callback: Function) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
class FailMockSocket extends MockSocket {
|
||||
connect(port: number, host: string, callback: Function) {
|
||||
this._dispatch("error", {});
|
||||
}
|
||||
}
|
||||
|
||||
function makeCache(hostname = "1.2.3.4") {
|
||||
fs.writeFileSync(adapter._sessionFilePath, hostname, "utf-8");
|
||||
}
|
||||
|
||||
function monkeyPatch(obj: any, funcName: string, callback: Function, dontRestoreAfterFirstCall: boolean) {
|
||||
var orig = obj[funcName];
|
||||
|
||||
var patch = function () {
|
||||
var result = callback.apply(obj, arguments);
|
||||
if (!dontRestoreAfterFirstCall) {
|
||||
(<any>patch).restore();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
(<any>patch).restore = function () {
|
||||
obj[funcName] = orig;
|
||||
};
|
||||
(<any>patch).orig = orig;
|
||||
obj[funcName] = patch;
|
||||
}
|
||||
|
||||
var _socket = net.Socket;
|
||||
|
||||
function setup() {
|
||||
// Zipping sums up to a significant amount of time over several
|
||||
// tests, we will mock it by default and have tests opt-out by
|
||||
// calling .restore() on _compressPath if necessary
|
||||
monkeyPatch(adapter, "_compressPath", function () {
|
||||
return new Buffer("testdata");
|
||||
}, true);
|
||||
|
||||
// execSync is used to launch remote desktop which we don't want
|
||||
// to happen during tests. We will no-op it by default and have
|
||||
// tests opt-out if necessary
|
||||
monkeyPatch(cp, "execSync", function () { }, true);
|
||||
|
||||
// Disable retries, tests that are interested in testing the retry
|
||||
// logic will opt-out
|
||||
adapter._numRetries = 0;
|
||||
|
||||
// Delete cache file
|
||||
if (fs.existsSync(adapter._sessionFilePath)) {
|
||||
fs.unlinkSync(adapter._sessionFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
// Restore socket class in case it was mocked
|
||||
net.Socket = _socket;
|
||||
|
||||
// Restore execSync
|
||||
(<any>cp).execSync.restore();
|
||||
|
||||
// Restore any direct APIs on the Adapter
|
||||
Object.keys(adapter).forEach(key => {
|
||||
var member = (<any>adapter)[key];
|
||||
member.restore && member.restore();
|
||||
});
|
||||
|
||||
// Delete cache file
|
||||
if (fs.existsSync(adapter._sessionFilePath)) {
|
||||
fs.unlinkSync(adapter._sessionFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
describe("Adapter", function () {
|
||||
beforeEach(setup);
|
||||
afterEach(teardown);
|
||||
|
||||
describe("registerAndLaunchAppxManifest", function () {
|
||||
|
||||
describe("RDP scenarios", function () {
|
||||
it("should launch rdp when no cache file is found", function (done) {
|
||||
net.Socket = <any>SuccessMockSocket;
|
||||
monkeyPatch(cp, "execSync", function (cmd: string) {
|
||||
if (cmd.indexOf(".rdp") === -1) {
|
||||
assert.fail();
|
||||
}
|
||||
done();
|
||||
}, false);
|
||||
adapter.registerAndLaunchAppxManifest("test");
|
||||
});
|
||||
|
||||
it("should launch rdp when connecting to cached hostname fails", function (done) {
|
||||
makeCache();
|
||||
net.Socket = <any>FailMockSocket;
|
||||
monkeyPatch(cp, "execSync", function (cmd: string) {
|
||||
if (cmd.indexOf(".rdp") === -1) {
|
||||
assert.fail();
|
||||
}
|
||||
done();
|
||||
}, false);
|
||||
adapter.registerAndLaunchAppxManifest("test");
|
||||
});
|
||||
|
||||
it("should not launch rdp when connecting to cached hostname succeeds", function (done) {
|
||||
makeCache();
|
||||
net.Socket = <any>SuccessMockSocket;
|
||||
monkeyPatch(cp, "execSync", function (cmd: string) {
|
||||
assert.fail();
|
||||
}, false);
|
||||
|
||||
MockSocket.kidnapNextInstance(socket => {
|
||||
socket.write = function (buffer: Buffer) {
|
||||
done();
|
||||
return true;
|
||||
};
|
||||
});
|
||||
adapter.registerAndLaunchAppxManifest("test");
|
||||
|
||||
(<any>cp.execSync).restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Caching scenarios", function () {
|
||||
it("should connect to cached hostname if cache file exists", function (done) {
|
||||
var testHostname = "testHostname";
|
||||
makeCache(testHostname);
|
||||
net.Socket = <any>SuccessMockSocket;
|
||||
|
||||
MockSocket.kidnapNextInstance(socket => {
|
||||
socket.connect = function (port: number, host: string, callback: Function) {
|
||||
assert.equal(testHostname, host);
|
||||
done();
|
||||
};
|
||||
});
|
||||
adapter.registerAndLaunchAppxManifest("test");
|
||||
});
|
||||
|
||||
it("should create cache file if connection was successful", function (done) {
|
||||
net.Socket = <any>SuccessMockSocket;
|
||||
|
||||
MockSocket.kidnapNextInstance(socket => {
|
||||
socket.write = function (buffer: Buffer) {
|
||||
assert.ok(fs.existsSync(adapter._sessionFilePath));
|
||||
done();
|
||||
return true;
|
||||
};
|
||||
});
|
||||
adapter.registerAndLaunchAppxManifest("test");
|
||||
});
|
||||
|
||||
it("should delete cache file if connection failed", function (done) {
|
||||
net.Socket = <any>FailMockSocket;
|
||||
makeCache();
|
||||
|
||||
MockSocket.kidnapNextInstance(socket => {
|
||||
// This is the fail socket, after which we expect the adapter
|
||||
// to start over and create another socket
|
||||
assert.ok(fs.existsSync(adapter._sessionFilePath));
|
||||
MockSocket.kidnapNextInstance(socket => {
|
||||
// This is the retry attempt, at this point the cache file
|
||||
// should've been deleted
|
||||
assert.ok(!fs.existsSync(adapter._sessionFilePath));
|
||||
done();
|
||||
});
|
||||
});
|
||||
adapter.registerAndLaunchAppxManifest("test");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче