blob: 627afb91f117353084e13b68c6e7800fe8be0056 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: implementation_imports
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:fuchsia_logger/src/internal/_log_message.dart';
import 'package:logging/logging.dart';
import 'package:test/test.dart';
import 'package:zircon/zircon.dart';
// 15 sec in nanoseconds
const int _lookBackTimeGap = 15 * 1000 * 1000 * 1000;
const int _socketBufferLength = 2032;
const int _zxClockMonotonic = 0;
void main() {
/// the following tests are taken from the initial logging implementation.
/// They should remain to ensure backwards compatibility
group('legacy toBytes tests', () {
test('convert to bytes info', () {
final message = _makeMessage(Level.INFO, 'foo');
final bytes = message.toBytes();
final buffer = bytes.buffer.asUint8List();
_validateFixedBlock(buffer, 0, 1, 2);
expect(buffer[32], equals(4));
expect(utf8.decode(buffer.sublist(33, 37)), equals('TEST'));
expect(utf8.decode(buffer.sublist(38, 41)), equals('foo'));
expect(buffer[41], equals(0));
// Length should be 33 + 5 (TEST) + 4 (foo)
expect(bytes.lengthInBytes, equals(42));
});
test('convert to bytes exception', () {
final message =
_makeMessage(Level.SHOUT, 'error', error: Exception('cause'));
final bytes = message.toBytes();
final buffer = bytes.buffer.asUint8List();
_validateFixedBlock(buffer, 3, 1, 2);
expect(buffer[32], equals(4));
expect(utf8.decode(buffer.sublist(33, 37)), equals('TEST'));
int end = 37;
// dividing 0 byte
expect(buffer[end++], equals(0));
int start = end;
expect(utf8.decode(buffer.sublist(start)),
matches('error: Exception: cause'));
end = start + 23;
expect(buffer[end++], equals(0));
expect(bytes.lengthInBytes, equals(end));
});
test('convert to bytes with stack trace', () {
const errorMsg = 'this error message plus the stacktrace need to be '
'long enough to hit the max block size to validate that truncation of long '
'messages works properly';
final message = _makeMessage(Level.SEVERE, errorMsg,
error: Exception('because'),
stackTrace:
StackTrace.fromString('_STACK_START_ ${StackTrace.current}'));
final bytes = message.toBytes();
final buffer = bytes.buffer.asUint8List();
_validateFixedBlock(buffer, 2, 1, 2);
expect(buffer[32], equals(4));
expect(utf8.decode(buffer.sublist(33, 37)), equals('TEST'));
int end = 37;
// dividing 0 byte
expect(buffer[end++], equals(0));
String msg = utf8.decode(buffer.sublist(end));
expect(msg, startsWith('$errorMsg: Exception: because\n'));
expect(msg, matches('_STACK_START_'));
expect(buffer.length, equals(_socketBufferLength));
expect(
utf8.decode(
buffer.sublist(_socketBufferLength - 4, _socketBufferLength - 1)),
equals('...'));
expect(bytes.lengthInBytes, _socketBufferLength);
});
test('convert to bytes with tags', () {
final tags = ['tag1', 'tag2'];
final message = _makeMessage(
Level.FINE,
'bar',
tags: tags,
);
final bytes = message.toBytes();
final buffer = bytes.buffer.asUint8List();
_validateFixedBlock(buffer, -2, 1, 2);
expect(buffer[32], equals(4));
expect(utf8.decode(buffer.sublist(33, 37)), equals('TEST'));
int start = 37;
// verify the first tag
expect(buffer[start], equals(tags[0].length));
int end = start + buffer[start] + 1;
start++;
expect(utf8.decode(buffer.sublist(start, end)), equals(tags[0]));
// verify the second tag
start = end;
expect(buffer[start], equals(tags[1].length));
end = start + buffer[start] + 1;
start++;
expect(utf8.decode(buffer.sublist(start, end)), equals(tags[1]));
// dividing 0 byte
expect(buffer[end++], equals(0));
start = end;
expect(utf8.decode(buffer.sublist(start, start + 3)), equals('bar'));
end = start + 3;
expect(buffer[end++], equals(0));
expect(bytes.lengthInBytes, equals(end));
});
});
}
List<String> _allTags(String name, List<String> tags) =>
(name != null ? [name] : [])..addAll(tags ?? []);
/// Convert from little endian format bytes to an unsiged 32 bit int.
int _bytesToInt32(List<int> bytes) {
ByteData byteData = ByteData(4);
for (int i = 0; i < 4; i++) {
byteData.setInt8(i, bytes[i]);
}
return byteData.getInt32(0, Endian.little);
}
/// Convert from little endian format bytes to an unsiged 64 bit int.
int _bytesToUint64(List<int> bytes) {
ByteData byteData = ByteData(8);
for (int i = 0; i < 8; i++) {
byteData.setInt8(i, bytes[i]);
}
return byteData.getUint64(0, Endian.little);
}
LogMessage _makeMessage(Level level, String message,
{int processId = 1,
int threadId = 2,
String name = 'TEST',
Object error,
StackTrace stackTrace,
List<String> tags}) =>
LogMessage(
record: LogRecord(level, message, null, error, stackTrace),
processId: processId,
threadId: threadId,
tags: _allTags(name, tags),
);
void _validateFixedBlock(
List<int> data, int level, int processId, int threadId) {
expect(_bytesToUint64(data), equals(processId));
expect(_bytesToUint64(data.sublist(8, 16)), equals(threadId));
// Log time should be within the last 30 seconds
int nowNanos = Platform.isFuchsia
? System.clockGet(_zxClockMonotonic)
: DateTime.now().microsecondsSinceEpoch * 1000;
int logNanos = _bytesToUint64(data.sublist(16, 24));
expect(logNanos, lessThanOrEqualTo(nowNanos));
expect(logNanos + _lookBackTimeGap, greaterThan(nowNanos));
expect(_bytesToInt32(data.sublist(24, 28)), equals(level));
}