Skip to content
This repository was archived by the owner on Feb 2, 2021. It is now read-only.

Commit f916772

Browse files
authored
detach logcat helper on stop application + tests (#1011)
* detach logcat helper on stop application + tests * fix getcontent bat script fix logcat helper test * enabled all tests * update after review * update logic * removed unused private field * made lint happy
1 parent 418ef56 commit f916772

File tree

5 files changed

+450
-13
lines changed

5 files changed

+450
-13
lines changed

definitions/mobile.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ declare module Mobile {
138138

139139
interface ILogcatHelper {
140140
start(deviceIdentifier: string): Promise<void>;
141+
stop(deviceIdentifier: string): void;
141142
}
142143

143144
/**

mobile/android/android-application-manager.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export class AndroidApplicationManager extends ApplicationManagerBase {
8383
}
8484

8585
public stopApplication(appIdentifier: string): Promise<void> {
86+
this.$logcatHelper.stop(this.identifier);
8687
return this.adb.executeShellCommand(["am", "force-stop", `${appIdentifier}`]);
8788
}
8889

mobile/android/logcat-helper.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,41 @@
11
import byline = require("byline");
22
import { DeviceAndroidDebugBridge } from "./device-android-debug-bridge";
3+
import { ChildProcess } from "child_process";
4+
5+
interface IDeviceLoggingData {
6+
loggingProcess: ChildProcess;
7+
lineStream: any;
8+
}
39

410
export class LogcatHelper implements Mobile.ILogcatHelper {
5-
private mapDeviceToLoggingStarted: IDictionary<boolean>;
11+
private mapDevicesLoggingData: IDictionary<IDeviceLoggingData>;
612

713
constructor(private $deviceLogProvider: Mobile.IDeviceLogProvider,
814
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
915
private $logger: ILogger,
1016
private $injector: IInjector,
1117
private $processService: IProcessService) {
12-
this.mapDeviceToLoggingStarted = Object.create(null);
18+
this.mapDevicesLoggingData = Object.create(null);
1319
}
1420

1521
public async start(deviceIdentifier: string): Promise<void> {
16-
if (deviceIdentifier && !this.mapDeviceToLoggingStarted[deviceIdentifier]) {
22+
if (deviceIdentifier && !this.mapDevicesLoggingData[deviceIdentifier]) {
1723
const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier });
1824

19-
// remove cached logs:
20-
await adb.executeCommand(["logcat", "-c"]);
21-
22-
const adbLogcat = await adb.executeCommand(["logcat"], { returnChildProcess: true });
23-
const lineStream = byline(adbLogcat.stdout);
25+
const logcatStream = await adb.executeCommand(["logcat"], { returnChildProcess: true });
26+
const lineStream = byline(logcatStream.stdout);
27+
this.mapDevicesLoggingData[deviceIdentifier] = {
28+
loggingProcess: logcatStream,
29+
lineStream: lineStream
30+
};
2431

25-
adbLogcat.stderr.on("data", (data: NodeBuffer) => {
32+
logcatStream.stderr.on("data", (data: NodeBuffer) => {
2633
this.$logger.trace("ADB logcat stderr: " + data.toString());
2734
});
2835

29-
adbLogcat.on("close", (code: number) => {
36+
logcatStream.on("close", (code: number) => {
3037
try {
31-
this.mapDeviceToLoggingStarted[deviceIdentifier] = false;
38+
this.stop(deviceIdentifier);
3239
if (code !== 0) {
3340
this.$logger.trace("ADB process exited with code " + code.toString());
3441
}
@@ -42,10 +49,16 @@ export class LogcatHelper implements Mobile.ILogcatHelper {
4249
this.$deviceLogProvider.logData(lineText, this.$devicePlatformsConstants.Android, deviceIdentifier);
4350
});
4451

45-
this.$processService.attachToProcessExitSignals(this, adbLogcat.kill);
52+
this.$processService.attachToProcessExitSignals(this, logcatStream.kill);
53+
}
54+
}
4655

47-
this.mapDeviceToLoggingStarted[deviceIdentifier] = true;
56+
public stop(deviceIdentifier: string): void {
57+
if (this.mapDevicesLoggingData[deviceIdentifier]) {
58+
this.mapDevicesLoggingData[deviceIdentifier].loggingProcess.removeAllListeners();
59+
this.mapDevicesLoggingData[deviceIdentifier].lineStream.removeAllListeners();
4860
}
61+
delete this.mapDevicesLoggingData[deviceIdentifier];
4962
}
5063
}
5164

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { LogcatHelper } from "../../../../mobile/android/logcat-helper";
2+
import { Yok } from "../../../../yok";
3+
import { assert } from "chai";
4+
import * as path from "path";
5+
import * as childProcess from "child_process";
6+
7+
class ChildProcessStub {
8+
public static methodCallCount = 0;
9+
private isWin = /^win/.test(process.platform);
10+
11+
public spawn(command: string, args?: string[], options?: any): childProcess.ChildProcess {
12+
ChildProcessStub.methodCallCount++;
13+
let pathToExecutable = "";
14+
if (this.isWin) {
15+
pathToExecutable = "type";
16+
} else {
17+
pathToExecutable = "cat";
18+
}
19+
pathToExecutable = path.join(pathToExecutable);
20+
const pathToSample = path.join(__dirname, "valid-sample.txt");
21+
return childProcess.spawn(pathToExecutable, [pathToSample]);
22+
}
23+
}
24+
25+
function createTestInjector(): IInjector {
26+
const injector = new Yok();
27+
injector.register("injector", injector);
28+
injector.register("logcatHelper", LogcatHelper);
29+
injector.register("logger", {
30+
debug(formatStr?: any, ...args: any[]): void {
31+
//left blank intentionally because of lint
32+
},
33+
trace(formatStr?: any, ...args: any[]): void {
34+
if (formatStr && formatStr.indexOf("ADB") !== -1) {
35+
//loghelper failed or socket closed"
36+
assert.isTrue(false);
37+
}
38+
}
39+
});
40+
injector.register("errors", {});
41+
injector.register("devicePlatformsConstants", { Android: "Android" });
42+
injector.register("processService", {
43+
attachToProcessExitSignals(context: any, callback: () => void): void {
44+
//left blank intentionally because of lint
45+
},
46+
});
47+
injector.register("deviceLogProvider", {
48+
logData(line: string, platform: string, deviceIdentifier: string): void {
49+
//left blank intentionally because of lint
50+
},
51+
});
52+
injector.register("childProcess", ChildProcessStub);
53+
injector.register("staticConfig", {
54+
async getAdbFilePath(): Promise<string> {
55+
return "";
56+
}
57+
});
58+
injector.register("androidDebugBridgeResultHandler", {});
59+
60+
return injector;
61+
}
62+
63+
describe("logcat-helper", () => {
64+
let injector: IInjector;
65+
let adbLogcatCrashedOrClosed: boolean;
66+
let loggedData: string[];
67+
68+
beforeEach(() => {
69+
adbLogcatCrashedOrClosed = false;
70+
injector = createTestInjector();
71+
loggedData = [];
72+
ChildProcessStub.methodCallCount = 0;
73+
});
74+
75+
describe("start", () => {
76+
it("whole logcat is read correctly", (done: mocha.Done) => {
77+
injector.register("deviceLogProvider", {
78+
logData(line: string, platform: string, deviceIdentifier: string): void {
79+
loggedData.push(line);
80+
if (line === "end") {
81+
assert.isAbove(loggedData.length, 0);
82+
done();
83+
}
84+
}
85+
});
86+
87+
const logcatHelper = injector.resolve<LogcatHelper>("logcatHelper");
88+
logcatHelper.start("valid-identifier");
89+
});
90+
it("if loghelper start is called multiple times with the same identifier it's a noop the second time", async () => {
91+
const logcatHelper = injector.resolve<LogcatHelper>("logcatHelper");
92+
await logcatHelper.start("valid-identifier");
93+
assert.equal(ChildProcessStub.methodCallCount, 1);
94+
await logcatHelper.start("valid-identifier");
95+
assert.equal(ChildProcessStub.methodCallCount, 1);
96+
await logcatHelper.start("valid-identifier");
97+
assert.equal(ChildProcessStub.methodCallCount, 1);
98+
});
99+
it("if loghelper start works when it's called multiple times with different identifiers", async () => {
100+
const logcatHelper = injector.resolve<LogcatHelper>("logcatHelper");
101+
await logcatHelper.start("valid-identifier1");
102+
assert.equal(ChildProcessStub.methodCallCount, 1);
103+
await logcatHelper.start("valid-identifier2");
104+
assert.equal(ChildProcessStub.methodCallCount, 2);
105+
await logcatHelper.start("valid-identifier3");
106+
assert.equal(ChildProcessStub.methodCallCount, 3);
107+
});
108+
});
109+
describe("stop", () => {
110+
it("device identifier is cleared on stop", async () => {
111+
const logcatHelper = injector.resolve<LogcatHelper>("logcatHelper");
112+
await logcatHelper.start("valid-identifier");
113+
assert.equal(ChildProcessStub.methodCallCount, 1);
114+
await logcatHelper.stop("valid-identifier");
115+
await logcatHelper.start("valid-identifier");
116+
assert.equal(ChildProcessStub.methodCallCount, 2);
117+
});
118+
it("stop doesn't blow up if called multiple times", async () => {
119+
const logcatHelper = injector.resolve<LogcatHelper>("logcatHelper");
120+
await logcatHelper.stop("valid-identifier");
121+
await logcatHelper.stop("valid-identifier");
122+
assert.isTrue(true);
123+
});
124+
});
125+
});

0 commit comments

Comments
 (0)