add test coverage for log processing
Summary: as per title Reviewed By: passy Differential Revision: D13376522 fbshipit-source-id: 4746d3234a13abdf81a23387e57d877714c94b44
This commit is contained in:
committed by
Facebook Github Bot
parent
4546e64509
commit
cbfbd0dd98
58
src/plugins/logs/__tests__/index.node.js
Normal file
58
src/plugins/logs/__tests__/index.node.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {addEntriesToState, processEntry} from '../index';
|
||||||
|
|
||||||
|
const entry = {
|
||||||
|
tag: 'OpenGLRenderer',
|
||||||
|
pid: 18384,
|
||||||
|
|
||||||
|
tid: 18409,
|
||||||
|
message: 'Swap behavior 1',
|
||||||
|
date: new Date('Feb 28 2013 19:00:00 EST'),
|
||||||
|
type: 'debug',
|
||||||
|
};
|
||||||
|
|
||||||
|
test('processEntry', () => {
|
||||||
|
const key = 'key';
|
||||||
|
const processedEntry = processEntry(entry, key);
|
||||||
|
expect(processedEntry.entry).toEqual(entry);
|
||||||
|
expect(processedEntry.row.key).toBe(key);
|
||||||
|
expect(typeof processedEntry.row.height).toBe('number');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('addEntriesToState without current state', () => {
|
||||||
|
const processedEntry = processEntry(entry, 'key');
|
||||||
|
const newState = addEntriesToState([processedEntry]);
|
||||||
|
|
||||||
|
expect(newState.rows.length).toBe(1);
|
||||||
|
expect(newState.entries.length).toBe(1);
|
||||||
|
expect(newState.entries[0]).toEqual(processedEntry);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('addEntriesToState with current state', () => {
|
||||||
|
const currentState = addEntriesToState([processEntry(entry, 'key1')]);
|
||||||
|
const processedEntry = processEntry(
|
||||||
|
{
|
||||||
|
...entry,
|
||||||
|
message: 'new message',
|
||||||
|
},
|
||||||
|
'key2',
|
||||||
|
);
|
||||||
|
const newState = addEntriesToState([processedEntry], currentState);
|
||||||
|
expect(newState.rows.length).toBe(2);
|
||||||
|
expect(newState.entries.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('addEntriesToState increase counter on duplicate message', () => {
|
||||||
|
const currentState = addEntriesToState([processEntry(entry, 'key1')]);
|
||||||
|
const processedEntry = processEntry(entry, 'key2');
|
||||||
|
const newState = addEntriesToState([processedEntry], currentState);
|
||||||
|
expect(newState.rows.length).toBe(1);
|
||||||
|
expect(newState.entries.length).toBe(2);
|
||||||
|
expect(newState.rows[0].columns.type.value.props.children).toBe(2);
|
||||||
|
});
|
||||||
@@ -243,6 +243,136 @@ function pad(chunk: mixed, len: number): string {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addEntriesToState(
|
||||||
|
items: Entries,
|
||||||
|
state: $Shape<State> = {
|
||||||
|
rows: [],
|
||||||
|
entries: [],
|
||||||
|
key2entry: {},
|
||||||
|
},
|
||||||
|
): $Shape<State> {
|
||||||
|
const rows = [...state.rows];
|
||||||
|
const entries = [...state.entries];
|
||||||
|
const key2entry = {...state.key2entry};
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const {entry, row} = items[i];
|
||||||
|
entries.push({row, entry});
|
||||||
|
key2entry[row.key] = entry;
|
||||||
|
|
||||||
|
let previousEntry: ?DeviceLogEntry = null;
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
previousEntry = items[i - 1].entry;
|
||||||
|
} else if (state.rows.length > 0 && state.entries.length > 0) {
|
||||||
|
previousEntry = state.entries[state.entries.length - 1].entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
addRowIfNeeded(rows, row, entry, previousEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
entries,
|
||||||
|
rows,
|
||||||
|
key2entry,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addRowIfNeeded(
|
||||||
|
rows: Array<TableBodyRow>,
|
||||||
|
row: TableBodyRow,
|
||||||
|
entry: DeviceLogEntry,
|
||||||
|
previousEntry: ?DeviceLogEntry,
|
||||||
|
) {
|
||||||
|
const previousRow = rows.length > 0 ? rows[rows.length - 1] : null;
|
||||||
|
if (
|
||||||
|
previousRow &&
|
||||||
|
previousEntry &&
|
||||||
|
entry.message === previousEntry.message &&
|
||||||
|
entry.tag === previousEntry.tag &&
|
||||||
|
previousRow.type != null
|
||||||
|
) {
|
||||||
|
// duplicate log, increase counter
|
||||||
|
const count =
|
||||||
|
previousRow.columns.type.value &&
|
||||||
|
previousRow.columns.type.value.props &&
|
||||||
|
typeof previousRow.columns.type.value.props.children === 'number'
|
||||||
|
? previousRow.columns.type.value.props.children + 1
|
||||||
|
: 2;
|
||||||
|
const type = LOG_TYPES[previousRow.type] || LOG_TYPES.debug;
|
||||||
|
previousRow.columns.type.value = (
|
||||||
|
<LogCount backgroundColor={type.color}>{count}</LogCount>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function processEntry(
|
||||||
|
entry: DeviceLogEntry,
|
||||||
|
key: string,
|
||||||
|
): {
|
||||||
|
row: TableBodyRow,
|
||||||
|
entry: DeviceLogEntry,
|
||||||
|
} {
|
||||||
|
console.log(JSON.stringify(entry));
|
||||||
|
const {icon, style} = LOG_TYPES[(entry.type: string)] || LOG_TYPES.debug;
|
||||||
|
|
||||||
|
// clean message
|
||||||
|
const message = entry.message.trim();
|
||||||
|
entry.type === 'error';
|
||||||
|
|
||||||
|
// build the item, it will either be batched or added straight away
|
||||||
|
return {
|
||||||
|
entry,
|
||||||
|
row: {
|
||||||
|
columns: {
|
||||||
|
type: {
|
||||||
|
value: icon,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
value: (
|
||||||
|
<HiddenScrollText code={true}>
|
||||||
|
{entry.date.toTimeString().split(' ')[0] +
|
||||||
|
'.' +
|
||||||
|
pad(entry.date.getMilliseconds(), 3)}
|
||||||
|
</HiddenScrollText>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
value: <HiddenScrollText code={true}>{message}</HiddenScrollText>,
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
value: <HiddenScrollText code={true}>{entry.tag}</HiddenScrollText>,
|
||||||
|
isFilterable: true,
|
||||||
|
},
|
||||||
|
pid: {
|
||||||
|
value: (
|
||||||
|
<HiddenScrollText code={true}>{String(entry.pid)}</HiddenScrollText>
|
||||||
|
),
|
||||||
|
isFilterable: true,
|
||||||
|
},
|
||||||
|
tid: {
|
||||||
|
value: (
|
||||||
|
<HiddenScrollText code={true}>{String(entry.tid)}</HiddenScrollText>
|
||||||
|
),
|
||||||
|
isFilterable: true,
|
||||||
|
},
|
||||||
|
app: {
|
||||||
|
value: <HiddenScrollText code={true}>{entry.app}</HiddenScrollText>,
|
||||||
|
isFilterable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
height: getLineCount(message) * 15 + 10, // 15px per line height + 8px padding
|
||||||
|
style,
|
||||||
|
type: entry.type,
|
||||||
|
filterValue: entry.message,
|
||||||
|
key,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default class LogTable extends FlipperDevicePlugin<
|
export default class LogTable extends FlipperDevicePlugin<
|
||||||
State,
|
State,
|
||||||
Actions,
|
Actions,
|
||||||
@@ -322,8 +452,10 @@ export default class LogTable extends FlipperDevicePlugin<
|
|||||||
supportedColumns.includes(obj.key),
|
supportedColumns.includes(obj.key),
|
||||||
);
|
);
|
||||||
|
|
||||||
const initialState = this.addEntriesToState(
|
const initialState = addEntriesToState(
|
||||||
this.device.getLogs().map(this.processEntry),
|
this.device
|
||||||
|
.getLogs()
|
||||||
|
.map(log => processEntry(log, String(this.counter++))),
|
||||||
);
|
);
|
||||||
this.state = {
|
this.state = {
|
||||||
...initialState,
|
...initialState,
|
||||||
@@ -335,7 +467,7 @@ export default class LogTable extends FlipperDevicePlugin<
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.logListener = this.device.addLogListener((entry: DeviceLogEntry) => {
|
this.logListener = this.device.addLogListener((entry: DeviceLogEntry) => {
|
||||||
const processedEntry = this.processEntry(entry);
|
const processedEntry = processEntry(entry, String(this.counter++));
|
||||||
this.incrementCounterIfNeeded(processedEntry.entry);
|
this.incrementCounterIfNeeded(processedEntry.entry);
|
||||||
this.scheudleEntryForBatch(processedEntry);
|
this.scheudleEntryForBatch(processedEntry);
|
||||||
});
|
});
|
||||||
@@ -365,73 +497,6 @@ export default class LogTable extends FlipperDevicePlugin<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
processEntry = (
|
|
||||||
entry: DeviceLogEntry,
|
|
||||||
): {
|
|
||||||
row: TableBodyRow,
|
|
||||||
entry: DeviceLogEntry,
|
|
||||||
} => {
|
|
||||||
const {icon, style} = LOG_TYPES[(entry.type: string)] || LOG_TYPES.debug;
|
|
||||||
|
|
||||||
// clean message
|
|
||||||
const message = entry.message.trim();
|
|
||||||
entry.type === 'error';
|
|
||||||
|
|
||||||
// build the item, it will either be batched or added straight away
|
|
||||||
return {
|
|
||||||
entry,
|
|
||||||
row: {
|
|
||||||
columns: {
|
|
||||||
type: {
|
|
||||||
value: icon,
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
time: {
|
|
||||||
value: (
|
|
||||||
<HiddenScrollText code={true}>
|
|
||||||
{entry.date.toTimeString().split(' ')[0] +
|
|
||||||
'.' +
|
|
||||||
pad(entry.date.getMilliseconds(), 3)}
|
|
||||||
</HiddenScrollText>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
value: <HiddenScrollText code={true}>{message}</HiddenScrollText>,
|
|
||||||
},
|
|
||||||
tag: {
|
|
||||||
value: <HiddenScrollText code={true}>{entry.tag}</HiddenScrollText>,
|
|
||||||
isFilterable: true,
|
|
||||||
},
|
|
||||||
pid: {
|
|
||||||
value: (
|
|
||||||
<HiddenScrollText code={true}>
|
|
||||||
{String(entry.pid)}
|
|
||||||
</HiddenScrollText>
|
|
||||||
),
|
|
||||||
isFilterable: true,
|
|
||||||
},
|
|
||||||
tid: {
|
|
||||||
value: (
|
|
||||||
<HiddenScrollText code={true}>
|
|
||||||
{String(entry.tid)}
|
|
||||||
</HiddenScrollText>
|
|
||||||
),
|
|
||||||
isFilterable: true,
|
|
||||||
},
|
|
||||||
app: {
|
|
||||||
value: <HiddenScrollText code={true}>{entry.app}</HiddenScrollText>,
|
|
||||||
isFilterable: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
height: getLineCount(message) * 15 + 10, // 15px per line height + 8px padding
|
|
||||||
style,
|
|
||||||
type: entry.type,
|
|
||||||
filterValue: entry.message,
|
|
||||||
key: String(this.counter++),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
scheudleEntryForBatch = (item: {
|
scheudleEntryForBatch = (item: {
|
||||||
row: TableBodyRow,
|
row: TableBodyRow,
|
||||||
entry: DeviceLogEntry,
|
entry: DeviceLogEntry,
|
||||||
@@ -448,46 +513,11 @@ export default class LogTable extends FlipperDevicePlugin<
|
|||||||
const thisBatch = this.batch;
|
const thisBatch = this.batch;
|
||||||
this.batch = [];
|
this.batch = [];
|
||||||
this.queued = false;
|
this.queued = false;
|
||||||
this.setState(state => this.addEntriesToState(thisBatch, state));
|
this.setState(state => addEntriesToState(thisBatch, state));
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
addEntriesToState = (
|
|
||||||
items: Entries,
|
|
||||||
state: $Shape<State> = {
|
|
||||||
rows: [],
|
|
||||||
entries: [],
|
|
||||||
key2entry: {},
|
|
||||||
},
|
|
||||||
): $Shape<State> => {
|
|
||||||
const rows = [...state.rows];
|
|
||||||
const entries = [...state.entries];
|
|
||||||
const key2entry = {...state.key2entry};
|
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
const {entry, row} = items[i];
|
|
||||||
entries.push({row, entry});
|
|
||||||
key2entry[row.key] = entry;
|
|
||||||
|
|
||||||
let previousEntry: ?DeviceLogEntry = null;
|
|
||||||
|
|
||||||
if (i > 0) {
|
|
||||||
previousEntry = items[i - 1].entry;
|
|
||||||
} else if (state.rows.length > 0 && state.entries.length > 0) {
|
|
||||||
previousEntry = state.entries[state.entries.length - 1].entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addRowIfNeeded(rows, row, entry, previousEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
entries,
|
|
||||||
rows,
|
|
||||||
key2entry,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.batchTimer) {
|
if (this.batchTimer) {
|
||||||
clearTimeout(this.batchTimer);
|
clearTimeout(this.batchTimer);
|
||||||
@@ -498,36 +528,6 @@ export default class LogTable extends FlipperDevicePlugin<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addRowIfNeeded(
|
|
||||||
rows: Array<TableBodyRow>,
|
|
||||||
row: TableBodyRow,
|
|
||||||
entry: DeviceLogEntry,
|
|
||||||
previousEntry: ?DeviceLogEntry,
|
|
||||||
) {
|
|
||||||
const previousRow = rows.length > 0 ? rows[rows.length - 1] : null;
|
|
||||||
if (
|
|
||||||
previousRow &&
|
|
||||||
previousEntry &&
|
|
||||||
entry.message === previousEntry.message &&
|
|
||||||
entry.tag === previousEntry.tag &&
|
|
||||||
previousRow.type != null
|
|
||||||
) {
|
|
||||||
// duplicate log, increase counter
|
|
||||||
const count =
|
|
||||||
previousRow.columns.type.value &&
|
|
||||||
previousRow.columns.type.value.props &&
|
|
||||||
typeof previousRow.columns.type.value.props.children === 'number'
|
|
||||||
? previousRow.columns.type.value.props.children + 1
|
|
||||||
: 2;
|
|
||||||
const type = LOG_TYPES[previousRow.type] || LOG_TYPES.debug;
|
|
||||||
previousRow.columns.type.value = (
|
|
||||||
<LogCount backgroundColor={type.color}>{count}</LogCount>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLogs = () => {
|
clearLogs = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
entries: [],
|
entries: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user