Skip to content

Commit 8b7795f

Browse files
committed
test: add more tests for android livesync tool
1 parent d8ab730 commit 8b7795f

File tree

1 file changed

+219
-33
lines changed

1 file changed

+219
-33
lines changed

test/services/livesync/android-livesync-tool.ts

Lines changed: 219 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,64 @@ import * as sinon from "sinon";
44
import { LoggerStub, ProcessServiceStub } from "../../stubs";
55
import { AndroidLivesyncTool } from "../../../lib/services/livesync/android-livesync-tool";
66
import { MobileHelper } from "../../../lib/common/mobile/mobile-helper";
7+
import { FileSystem } from "../../../lib/common/file-system";
78
import * as net from "net";
8-
import { Duplex } from "stream";
99
import { MobilePlatformsCapabilities } from "../../../lib/mobile-platforms-capabilities";
1010
import { DevicePlatformsConstants } from "../../../lib/common/mobile/device-platforms-constants";
11+
import * as path from "path";
12+
import temp = require("temp");
13+
import * as crypto from "crypto";
1114

12-
const createTestInjector = (socket: INetSocket): IInjector => {
13-
const testInjector = new Yok();
15+
temp.track();
16+
const protocolVersion = "0.2.0";
17+
18+
class TestSocket extends net.Socket {
19+
public accomulatedData: Buffer[] = [];
20+
public connect(){}
21+
public write(data: Buffer | string, cb?: string | Function, encoding?: Function | string): boolean {
22+
if(data instanceof Buffer) {
23+
this.accomulatedData.push(data);
24+
} else {
25+
const buffer = Buffer.from(data, 'utf8');
26+
this.accomulatedData.push(buffer);
27+
}
1428

15-
testInjector.register("fs", {
16-
createReadStream: (filename: string, encoding?: IReadFileOptions | string): NodeJS.ReadableStream => {
17-
const fileStream = new Duplex();
18-
fileStream.write("testContent");
19-
fileStream.end();
20-
return fileStream;
21-
},
29+
if(cb && cb instanceof Function){
30+
cb.call(this);
31+
}
2232

23-
exists: (): boolean => true,
33+
return true;
34+
}
35+
}
2436

25-
readJson: (filePath: string): any => null,
37+
const rootTestFileJs = "test.js";
38+
const rootTestFileCss = "test.css";
39+
const dirTestFileJs = path.join("testdir", "testdir.js");
40+
const dirTestFileCss = path.join("testdir", "testdir.css");
2641

27-
enumerateFilesInDirectorySync: (directoryPath: string,
28-
filterCallback?: (_file: string, _stat: IFsStats) => boolean,
29-
opts?: { enumerateDirectories?: boolean, includeEmptyDirectories?: boolean },
30-
foundFiles?: string[]): string[] => []
31-
});
42+
const fileContents = {
43+
[rootTestFileJs]: "Test js content",
44+
[rootTestFileCss]: "Test css content",
45+
[dirTestFileJs]: "Test js in dir",
46+
[dirTestFileCss]: "Test css in dir"
47+
}
48+
49+
const projectCreated = false;
50+
const testAppPath = temp.mkdirSync("testsyncapp");
51+
const testAppPlatformPath = path.join(testAppPath, "platforms", "android", "app", "src", "main", "assets", "app");
52+
const createTestProject = (testInjector: IInjector) => {
53+
if(!projectCreated) {
54+
const fs = testInjector.resolve("fs");
55+
_.forEach(fileContents, (value, key) => {
56+
fs.writeFile(path.join(testAppPlatformPath, key), value);
57+
})
58+
}
59+
}
60+
61+
const createTestInjector = (socket: INetSocket, fileStreams: IDictionary<NodeJS.ReadableStream>): IInjector => {
62+
const testInjector = new Yok();
3263

64+
testInjector.register("fs", FileSystem);
3365
testInjector.register("logger", LoggerStub);
3466
testInjector.register("processService", ProcessServiceStub);
3567
testInjector.register("injector", testInjector);
@@ -49,13 +81,77 @@ const createTestInjector = (socket: INetSocket): IInjector => {
4981
return testInjector;
5082
};
5183

84+
const getSendFileData = (buffers: Buffer[]) => {
85+
const buffer = Buffer.concat(buffers);
86+
const operation = getOperation(buffer);
87+
const { fileName, fileNameEnd } = getFileName(buffer);
88+
89+
const { fileContentSize, fileContentSizeEnd } = getFileContentSize(buffer, fileNameEnd);
90+
const headerHashMatch = compareHash(buffer, 0, fileContentSizeEnd, fileContentSizeEnd)
91+
const headerHashEnd = fileContentSizeEnd + 16;
92+
const {fileContent, fileContentEnd} = getFileContent(buffer, headerHashEnd, fileContentSize);
93+
const fileHashMatch = compareHash(buffer, headerHashEnd, fileContentEnd, fileContentEnd);
94+
95+
return {operation, fileName, fileContent, headerHashMatch, fileHashMatch};
96+
}
97+
98+
const getRemoveFileData = (buffers: Buffer[]) => {
99+
const buffer = Buffer.concat(buffers);
100+
const operation = getOperation(buffer);
101+
const { fileName, fileNameEnd } = getFileName(buffer);
102+
const headerHashMatch = compareHash(buffer, 0, fileNameEnd, fileNameEnd)
103+
104+
return { operation, fileName, headerHashMatch};
105+
}
106+
107+
const getFileName = (buffer: Buffer) => {
108+
const fileNameSizeLength = buffer.readUInt8(1);
109+
const fileNameSizeEnd = fileNameSizeLength + 2;
110+
const fileNameSize = buffer.toString("utf8", 2, fileNameSizeEnd);
111+
const fileNameEnd = fileNameSizeEnd + Number(fileNameSize)
112+
const fileName = buffer.toString("utf8", fileNameSizeEnd, fileNameEnd);
113+
114+
return { fileName, fileNameEnd };
115+
}
116+
117+
const getFileContentSize = (buffer: Buffer, offset: number) => {
118+
const fileContentSizeLength = buffer.readUInt8(offset);
119+
const fileContentSizeBegin = offset + 1;
120+
const fileContentSizeEnd = fileContentSizeBegin + fileContentSizeLength;
121+
const fileContentSize = Number(buffer.toString("utf8", fileContentSizeBegin, fileContentSizeEnd));
122+
123+
return {fileContentSize, fileContentSizeEnd}
124+
}
125+
126+
const getFileContent = (buffer: Buffer, offset: number, contentLength: number) => {
127+
const fileContentEnd = offset + Number(contentLength)
128+
const fileContent = buffer.toString("utf8", offset, fileContentEnd);
129+
130+
return {fileContent, fileContentEnd};
131+
}
132+
133+
const getOperation = (buffer: Buffer) => {
134+
const operation = buffer.toString("utf8",0 , 1);
135+
136+
return Number(operation);
137+
}
138+
139+
const compareHash = (buffer: Buffer, dataStart: number, dataEnd: number, hashStart: number) => {
140+
const headerBuffer = buffer.slice(dataStart, dataEnd);
141+
const hashEnd = hashStart + 16;
142+
const headerHash = buffer.slice(hashStart, hashEnd);
143+
const computedHash = crypto.createHash("md5").update(headerBuffer).digest();
144+
const headerHashMatch = headerHash.equals(computedHash);
145+
146+
return headerHashMatch;
147+
}
148+
52149
const getHandshakeBuffer = () => {
53-
const protocolVersion = "0.2.0";
54150
const packageName = "org.comp.test";
55151
const handshakeBuffer = Buffer.alloc(Buffer.byteLength(protocolVersion) + Buffer.byteLength(packageName) + 1);
56152

57153
let offset = handshakeBuffer.writeUInt8(Buffer.byteLength(protocolVersion), 0);
58-
offset =+ handshakeBuffer.write(protocolVersion, offset);
154+
offset = offset + handshakeBuffer.write(protocolVersion, offset);
59155
handshakeBuffer.write(packageName, offset);
60156

61157
return handshakeBuffer;
@@ -66,19 +162,14 @@ describe.only("AndroidLivesyncTool", () => {
66162
let livesyncTool: IAndroidLivesyncTool = null;
67163
let testSocket: INetSocket;
68164
let sandbox: sinon.SinonSandbox = null;
165+
let fileStreams: IDictionary<NodeJS.ReadableStream> = null;
69166

70167
beforeEach(() => {
71168
sandbox = sinon.sandbox.create();
72-
testSocket = new net.Socket();
73-
var stub = sandbox.stub(testSocket, 'write').callsFake(function () {
74-
75-
var args = stub.args;
76-
77-
// this will echo whatever they wrote
78-
if (args.length > 0)
79-
testSocket.emit('data', stub.args[stub.callCount - 1][0]);
80-
});
81-
testInjector = createTestInjector(testSocket);
169+
testSocket = new TestSocket();
170+
fileStreams = {};
171+
testInjector = createTestInjector(testSocket, fileStreams);
172+
createTestProject(testInjector);
82173
livesyncTool = testInjector.resolve(AndroidLivesyncTool);
83174
});
84175

@@ -98,7 +189,7 @@ describe.only("AndroidLivesyncTool", () => {
98189
if(event === "close") {
99190
closeAttachCount++;
100191
if(closeAttachCount === 1){
101-
testSocket.emit("close", false);
192+
testSocket.emit('close', false);
102193
}
103194
}
104195
});
@@ -108,7 +199,7 @@ describe.only("AndroidLivesyncTool", () => {
108199
if(event === "data") {
109200
dataAttachCount++
110201
if(dataAttachCount === 2){
111-
testSocket.write(getHandshakeBuffer());
202+
testSocket.emit('data', getHandshakeBuffer());
112203
}
113204
}
114205
});
@@ -117,15 +208,110 @@ describe.only("AndroidLivesyncTool", () => {
117208

118209
return connectPromise.then(() => {
119210
assert(connectStub.calledTwice);
211+
assert.isFulfilled(connectPromise);
212+
assert.equal(livesyncTool.protocolVersion, protocolVersion);
120213
}
121214
);
122215
});
123216

124217
it("should fail eventually", () => {
125-
sandbox.stub(testSocket, "connect");
126218
const connectPromise = livesyncTool.connect({ appIdentifier: "test", deviceIdentifier: "test", appPlatformsPath: "test", connectTimeout: 400 });
219+
220+
return assert.isRejected(connectPromise);
221+
});
222+
223+
it("should fail if connection alreday exists", () => {
224+
const originalOnce = testSocket.once;
225+
226+
sandbox.stub(testSocket, "once").callsFake(function(event: string) {
227+
originalOnce.apply(this, arguments);
228+
if(event === "data") {
229+
testSocket.emit('data', getHandshakeBuffer());
230+
}
231+
});
127232

128-
return connectPromise.should.be.rejected;
233+
const connectPromise = livesyncTool.connect({ appIdentifier: "test", deviceIdentifier: "test", appPlatformsPath: "test", connectTimeout: 400 }).then(() => {
234+
return livesyncTool.connect({ appIdentifier: "test", deviceIdentifier: "test", appPlatformsPath: "test", connectTimeout: 400 });
235+
});
236+
237+
return assert.isRejected(connectPromise);
238+
});
239+
});
240+
241+
describe("sendFile", () => {
242+
it("sends correct information", async () => {
243+
const originalOnce = testSocket.once;
244+
let dataAttachCount = 0;
245+
const filePath = path.join(testAppPlatformPath, rootTestFileJs);
246+
sandbox.stub(testSocket, "once").callsFake(function(event: string) {
247+
originalOnce.apply(this, arguments);
248+
if(event === "data") {
249+
dataAttachCount++
250+
if(dataAttachCount === 1){
251+
testSocket.emit('data', getHandshakeBuffer());
252+
}
253+
}
254+
});
255+
await livesyncTool.connect({ appIdentifier: "test", deviceIdentifier: "test", appPlatformsPath: testAppPlatformPath});
256+
257+
await livesyncTool.sendFile(filePath);
258+
259+
const sendFileData = getSendFileData((testSocket as TestSocket).accomulatedData);
260+
261+
assert.equal(sendFileData.fileContent, fileContents[rootTestFileJs]);
262+
assert.equal(sendFileData.fileName, rootTestFileJs);
263+
assert(sendFileData.headerHashMatch);
264+
assert(sendFileData.fileHashMatch);
265+
assert.equal(sendFileData.operation, AndroidLivesyncTool.CREATE_FILE_OPERATION);
266+
});
267+
});
268+
269+
describe("remove file", () => {
270+
it("sends correct information", async () => {
271+
const originalOnce = testSocket.once;
272+
let dataAttachCount = 0;
273+
const filePath = path.join(testAppPlatformPath, rootTestFileJs);
274+
sandbox.stub(testSocket, "once").callsFake(function(event: string) {
275+
originalOnce.apply(this, arguments);
276+
if(event === "data") {
277+
dataAttachCount++
278+
if(dataAttachCount === 1){
279+
testSocket.emit('data', getHandshakeBuffer());
280+
}
281+
}
282+
});
283+
await livesyncTool.connect({ appIdentifier: "test", deviceIdentifier: "test", appPlatformsPath: testAppPlatformPath});
284+
await livesyncTool.removeFile(filePath);
285+
286+
const removeData = getRemoveFileData((testSocket as TestSocket).accomulatedData);
287+
288+
assert.equal(removeData.fileName, rootTestFileJs);
289+
assert.equal(removeData.operation, AndroidLivesyncTool.DELETE_FILE_OPERATION);
290+
assert(removeData.headerHashMatch);
291+
});
292+
});
293+
294+
describe("sendFiles", () => {
295+
it("calls sendFile for each file", async () => {
296+
const filePaths = _.keys(fileContents).map(filePath => path.join(testAppPlatformPath, filePath));
297+
const sendFileStub = sandbox.stub(livesyncTool, "sendFile").callsFake(() => Promise.resolve());
298+
await livesyncTool.sendFiles(filePaths);
299+
300+
_.forEach(filePaths, (filePath) => {
301+
assert(sendFileStub.calledWith(filePath));
302+
})
303+
});
304+
});
305+
306+
describe("removeFiles", () => {
307+
it("calls sendFile for each file", async () => {
308+
const filePaths = _.keys(fileContents).map(filePath => path.join(testAppPlatformPath, filePath));
309+
const removeFileStub = sandbox.stub(livesyncTool, "removeFile").callsFake(() => Promise.resolve());
310+
await livesyncTool.removeFiles(filePaths);
311+
312+
_.forEach(filePaths, (filePath) => {
313+
assert(removeFileStub.calledWith(filePath));
314+
})
129315
});
130316
});
131317
});

0 commit comments

Comments
 (0)