Skip to content

Commit 4147180

Browse files
committed
fix: sync device logs
1 parent f2742d9 commit 4147180

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

src/logs/log-buffer.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { Emitter, AndroidEntry, IosEntry } from 'logkitten';
55
import { Level, logkitten } from 'logkitten';
66
import type { DetoxAllure2AdapterDeviceLogsOptions } from '../types';
77
import type { DeviceWrapper } from '../utils';
8+
import { Deferred } from '../utils';
89

910
import { PIDEntryCollection } from './pid-entry-collection';
1011

@@ -32,6 +33,7 @@ export class LogBuffer implements StepLogRecorder {
3233
private readonly _appEntries = new PIDEntryCollection();
3334
private readonly _detoxEntries = new PIDEntryCollection();
3435
private _options: DetoxAllure2AdapterDeviceLogsOptions;
36+
private readonly _deferreds = new Set<Deferred<number>>();
3537

3638
constructor(readonly _config: LogBufferOptions) {
3739
const deviceId = this._config.device.id;
@@ -102,8 +104,32 @@ export class LogBuffer implements StepLogRecorder {
102104
}
103105
};
104106

107+
private _updateDeferreds(ts: number) {
108+
for (const deferred of this._deferreds) {
109+
deferred.update(ts);
110+
}
111+
}
112+
113+
/**
114+
* Waits until a log entry with timestamp > reference is seen, or times out.
115+
* @param reference timestamp to compare against
116+
* @param timeoutMs timeout in ms (default 1000)
117+
*/
118+
public ensureIntegrity(reference: number, timeoutMs = 1000): Promise<void> {
119+
const deferred = new Deferred<number>({
120+
timeoutMs,
121+
predicate: (ts) => ts > reference,
122+
cleanup: () => {
123+
this._deferreds.delete(deferred);
124+
},
125+
});
126+
this._deferreds.add(deferred);
127+
return deferred.promise.then(() => {}); // resolve to void
128+
}
129+
105130
private readonly _onEntry = (entry: AnyEntry) => {
106131
this._appEntries.push(entry);
132+
this._updateDeferreds(entry.ts);
107133
};
108134

109135
private _iosFilter(entry: IosEntry): boolean {

src/utils/deferred.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
export class Deferred<T> {
2+
public promise: Promise<T>;
3+
private _resolve!: (value: T) => void;
4+
private _reject!: (reason?: any) => void;
5+
private _settled = false;
6+
private _timeout: NodeJS.Timeout;
7+
private _cleanup: () => void;
8+
private _predicate: (value: T) => boolean;
9+
10+
constructor({
11+
timeoutMs,
12+
predicate,
13+
cleanup,
14+
}: {
15+
timeoutMs: number;
16+
predicate: (value: T) => boolean;
17+
cleanup: () => void;
18+
}) {
19+
this._predicate = predicate;
20+
this._cleanup = cleanup;
21+
this.promise = new Promise<T>((resolve, reject) => {
22+
this._resolve = (value) => {
23+
if (!this._settled) {
24+
this._settled = true;
25+
clearTimeout(this._timeout);
26+
this._cleanup();
27+
resolve(value);
28+
}
29+
};
30+
this._reject = (reason) => {
31+
if (!this._settled) {
32+
this._settled = true;
33+
clearTimeout(this._timeout);
34+
this._cleanup();
35+
reject(reason);
36+
}
37+
};
38+
});
39+
this._timeout = setTimeout(() => {
40+
this._reject(new Error('Deferred timed out'));
41+
}, timeoutMs);
42+
}
43+
44+
update(value: T) {
45+
if (this._settled) return;
46+
if (this._predicate(value)) {
47+
this._resolve(value);
48+
}
49+
}
50+
}

src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export * from './deferred';
12
export * from './device-wrapper';
23
export * from './worker-wrapper';

0 commit comments

Comments
 (0)