| // 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)); |
| } |