Unverified Commit a871d591 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Apply basic log filtering and formatting to fuchsia logs. (#24143)

parent c8ccd692
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'context.dart';
/// The current system clock instance.
SystemClock get systemClock => context[SystemClock];
/// A class for making time based operations testable.
class SystemClock {
/// A const constructor to allow subclasses to be const.
const SystemClock();
/// Create a clock with a fixed current time.
const factory SystemClock.fixed(DateTime time) = _FixedTimeClock;
/// Retrieve the current time.
DateTime now() => DateTime.now();
/// Compute the time a given duration ago.
DateTime ago(Duration duration) {
return now().subtract(duration);
}
}
class _FixedTimeClock extends SystemClock {
const _FixedTimeClock(this._fixedTime);
final DateTime _fixedTime;
@override
DateTime now() => _fixedTime;
}
...@@ -8,7 +8,6 @@ import 'dart:math' show Random, max; ...@@ -8,7 +8,6 @@ import 'dart:math' show Random, max;
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:quiver/time.dart';
import '../globals.dart'; import '../globals.dart';
import 'context.dart'; import 'context.dart';
...@@ -251,8 +250,6 @@ Map<String, dynamic> castStringKeyedMap(dynamic untyped) { ...@@ -251,8 +250,6 @@ Map<String, dynamic> castStringKeyedMap(dynamic untyped) {
return map.cast<String, dynamic>(); return map.cast<String, dynamic>();
} }
Clock get clock => context[Clock];
typedef AsyncCallback = Future<void> Function(); typedef AsyncCallback = Future<void> Function();
/// A [Timer] inspired class that: /// A [Timer] inspired class that:
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/time.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
...@@ -300,7 +301,7 @@ class RunCommand extends RunCommandBase { ...@@ -300,7 +301,7 @@ class RunCommand extends RunCommandBase {
} catch (error) { } catch (error) {
throwToolExit(error.toString()); throwToolExit(error.toString());
} }
final DateTime appStartedTime = clock.now(); final DateTime appStartedTime = systemClock.now();
final int result = await app.runner.waitForAppToFinish(); final int result = await app.runner.waitForAppToFinish();
if (result != 0) if (result != 0)
throwToolExit(null, exitCode: result); throwToolExit(null, exitCode: result);
...@@ -391,7 +392,7 @@ class RunCommand extends RunCommandBase { ...@@ -391,7 +392,7 @@ class RunCommand extends RunCommandBase {
final Completer<void> appStartedTimeRecorder = Completer<void>.sync(); final Completer<void> appStartedTimeRecorder = Completer<void>.sync();
// This callback can't throw. // This callback can't throw.
appStartedTimeRecorder.future.then<void>( // ignore: unawaited_futures appStartedTimeRecorder.future.then<void>( // ignore: unawaited_futures
(_) { appStartedTime = clock.now(); } (_) { appStartedTime = systemClock.now(); }
); );
final int result = await runner.run( final int result = await runner.run(
......
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
import 'dart:async'; import 'dart:async';
import 'package:quiver/time.dart';
import 'android/android_sdk.dart'; import 'android/android_sdk.dart';
import 'android/android_studio.dart'; import 'android/android_studio.dart';
import 'android/android_workflow.dart'; import 'android/android_workflow.dart';
...@@ -19,6 +17,7 @@ import 'base/io.dart'; ...@@ -19,6 +17,7 @@ import 'base/io.dart';
import 'base/logger.dart'; import 'base/logger.dart';
import 'base/os.dart'; import 'base/os.dart';
import 'base/platform.dart'; import 'base/platform.dart';
import 'base/time.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'cache.dart'; import 'cache.dart';
import 'compile.dart'; import 'compile.dart';
...@@ -55,7 +54,6 @@ Future<T> runInContext<T>( ...@@ -55,7 +54,6 @@ Future<T> runInContext<T>(
AssetBundleFactory: () => AssetBundleFactory.defaultInstance, AssetBundleFactory: () => AssetBundleFactory.defaultInstance,
BotDetector: () => const BotDetector(), BotDetector: () => const BotDetector(),
Cache: () => Cache(), Cache: () => Cache(),
Clock: () => const Clock(),
CocoaPods: () => CocoaPods(), CocoaPods: () => CocoaPods(),
CocoaPodsValidator: () => const CocoaPodsValidator(), CocoaPodsValidator: () => const CocoaPodsValidator(),
Config: () => Config(), Config: () => Config(),
...@@ -68,7 +66,7 @@ Future<T> runInContext<T>( ...@@ -68,7 +66,7 @@ Future<T> runInContext<T>(
FuchsiaArtifacts: () => FuchsiaArtifacts(), FuchsiaArtifacts: () => FuchsiaArtifacts(),
FuchsiaWorkflow: () => FuchsiaWorkflow(), FuchsiaWorkflow: () => FuchsiaWorkflow(),
Flags: () => const EmptyFlags(), Flags: () => const EmptyFlags(),
FlutterVersion: () => FlutterVersion(const Clock()), FlutterVersion: () => FlutterVersion(const SystemClock()),
GenSnapshot: () => const GenSnapshot(), GenSnapshot: () => const GenSnapshot(),
HotRunnerConfig: () => HotRunnerConfig(), HotRunnerConfig: () => HotRunnerConfig(),
IMobileDevice: () => const IMobileDevice(), IMobileDevice: () => const IMobileDevice(),
...@@ -80,6 +78,7 @@ Future<T> runInContext<T>( ...@@ -80,6 +78,7 @@ Future<T> runInContext<T>(
OperatingSystemUtils: () => OperatingSystemUtils(), OperatingSystemUtils: () => OperatingSystemUtils(),
PlistBuddy: () => const PlistBuddy(), PlistBuddy: () => const PlistBuddy(),
SimControl: () => SimControl(), SimControl: () => SimControl(),
SystemClock: () => const SystemClock(),
Stdio: () => const Stdio(), Stdio: () => const Stdio(),
Usage: () => Usage(), Usage: () => Usage(),
Xcode: () => Xcode(), Xcode: () => Xcode(),
......
...@@ -12,6 +12,7 @@ import '../base/io.dart'; ...@@ -12,6 +12,7 @@ import '../base/io.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/process_manager.dart'; import '../base/process_manager.dart';
import '../base/time.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../device.dart'; import '../device.dart';
import '../globals.dart'; import '../globals.dart';
...@@ -25,23 +26,48 @@ final String _ipv6Loopback = InternetAddress.loopbackIPv6.address; ...@@ -25,23 +26,48 @@ final String _ipv6Loopback = InternetAddress.loopbackIPv6.address;
/// Read the log for a particular device. /// Read the log for a particular device.
class _FuchsiaLogReader extends DeviceLogReader { class _FuchsiaLogReader extends DeviceLogReader {
_FuchsiaLogReader(this._device); _FuchsiaLogReader(this._device, [this._app]);
// TODO(jonahwilliams): handle filtering log output from different modules. static final RegExp _flutterLogOutput = RegExp(r'INFO: \w+\(flutter\): ');
static final Pattern flutterLogOutput = RegExp(r'\[\d+\.\d+\]\[\d+\]\[\d+\]\[klog\] INFO: \w+\(flutter\): '); static final RegExp _utcDateOutput = RegExp(r'\d+\-\d+\-\d+ \d+:\d+:\d+');
FuchsiaDevice _device; FuchsiaDevice _device;
ApplicationPackage _app;
@override String get name => _device.name; @override String get name => _device.name;
Stream<String> _logLines; Stream<String> _logLines;
@override @override
Stream<String> get logLines { Stream<String> get logLines {
_logLines ??= fuchsiaSdk.syslogs() _logLines ??= _processLogs(fuchsiaSdk.syslogs());
.where((String line) => flutterLogOutput.matchAsPrefix(line) != null);
return _logLines; return _logLines;
} }
Stream<String> _processLogs(Stream<String> lines) async* {
// Get the starting time of the log processor to filter logs from before
// the process attached.
final DateTime startTime = systemClock.now();
// Determine if line comes from flutter, and optionally whether it matches
// the correct fuchsia module.
final RegExp matchRegExp = _app == null
? _flutterLogOutput
: RegExp('INFO: ${_app.name}\\(flutter\\): ');
await for (String line in lines.where(matchRegExp.hasMatch)) {
// Read out the date string from the log and compare it to the current time:
// Example: 2018-11-09 01:27:45
final String rawDate = _utcDateOutput.firstMatch(line)?.group(0);
if (rawDate == null) {
continue;
}
final DateTime logTime = DateTime.parse(rawDate);
if (logTime.millisecondsSinceEpoch < startTime.millisecondsSinceEpoch) {
continue;
}
// Format log into a useful string:
yield '[${logTime.toLocal()}] Flutter: ${line.split(matchRegExp).last}';
}
}
@override @override
String toString() => name; String toString() => name;
} }
...@@ -152,7 +178,7 @@ class FuchsiaDevice extends Device { ...@@ -152,7 +178,7 @@ class FuchsiaDevice extends Device {
Future<String> get sdkNameAndVersion async => 'Fuchsia'; Future<String> get sdkNameAndVersion async => 'Fuchsia';
@override @override
DeviceLogReader getLogReader({ApplicationPackage app}) => _logReader ??= _FuchsiaLogReader(this); DeviceLogReader getLogReader({ApplicationPackage app}) => _logReader ??= _FuchsiaLogReader(this, app);
_FuchsiaLogReader _logReader; _FuchsiaLogReader _logReader;
@override @override
...@@ -279,6 +305,13 @@ class _FuchsiaPortForwarder extends DevicePortForwarder { ...@@ -279,6 +305,13 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
} }
} }
class FuchsiaModulePackage extends ApplicationPackage {
FuchsiaModulePackage({@required this.name}) : super(id: name);
@override
final String name;
}
/// Parses output from `dart.services` output on a fuchsia device. /// Parses output from `dart.services` output on a fuchsia device.
/// ///
/// Example output: /// Example output:
......
...@@ -12,7 +12,6 @@ import '../base/io.dart'; ...@@ -12,7 +12,6 @@ import '../base/io.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/process_manager.dart'; import '../base/process_manager.dart';
import '../globals.dart';
/// The [FuchsiaSdk] instance. /// The [FuchsiaSdk] instance.
FuchsiaSdk get fuchsiaSdk => context[FuchsiaSdk]; FuchsiaSdk get fuchsiaSdk => context[FuchsiaSdk];
...@@ -27,7 +26,7 @@ FuchsiaArtifacts get fuchsiaArtifacts => context[FuchsiaArtifacts]; ...@@ -27,7 +26,7 @@ FuchsiaArtifacts get fuchsiaArtifacts => context[FuchsiaArtifacts];
class FuchsiaSdk { class FuchsiaSdk {
static const List<String> _netaddrCommand = <String>['fx', 'netaddr', '--fuchsia', '--nowait']; static const List<String> _netaddrCommand = <String>['fx', 'netaddr', '--fuchsia', '--nowait'];
static const List<String> _netlsCommand = <String>['fx', 'netls', '--nowait']; static const List<String> _netlsCommand = <String>['fx', 'netls', '--nowait'];
static const List<String> _syslogCommand = <String>['fx', 'syslog']; static const List<String> _syslogCommand = <String>['fx', 'syslog', '--clock', 'Local'];
/// Invokes the `netaddr` command. /// Invokes the `netaddr` command.
/// ///
...@@ -57,12 +56,11 @@ class FuchsiaSdk { ...@@ -57,12 +56,11 @@ class FuchsiaSdk {
process.kill(); process.kill();
}); });
processManager.start(_syslogCommand).then((Process newProcess) { processManager.start(_syslogCommand).then((Process newProcess) {
printTrace('Running logs');
if (controller.isClosed) { if (controller.isClosed) {
return; return;
} }
process = newProcess; process = newProcess;
process.exitCode.then((_) => controller.close); process.exitCode.whenComplete(controller.close);
controller.addStream(process.stdout.transform(utf8.decoder).transform(const LineSplitter())); controller.addStream(process.stdout.transform(utf8.decoder).transform(const LineSplitter()));
}); });
return controller.stream; return controller.stream;
......
...@@ -13,6 +13,7 @@ import '../application_package.dart'; ...@@ -13,6 +13,7 @@ import '../application_package.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/context.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/time.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../bundle.dart' as bundle; import '../bundle.dart' as bundle;
...@@ -320,7 +321,7 @@ abstract class FlutterCommand extends Command<void> { ...@@ -320,7 +321,7 @@ abstract class FlutterCommand extends Command<void> {
/// so that this method can record and report the overall time to analytics. /// so that this method can record and report the overall time to analytics.
@override @override
Future<void> run() { Future<void> run() {
final DateTime startTime = clock.now(); final DateTime startTime = systemClock.now();
return context.run<void>( return context.run<void>(
name: 'command', name: 'command',
...@@ -336,7 +337,7 @@ abstract class FlutterCommand extends Command<void> { ...@@ -336,7 +337,7 @@ abstract class FlutterCommand extends Command<void> {
commandResult = const FlutterCommandResult(ExitStatus.fail); commandResult = const FlutterCommandResult(ExitStatus.fail);
rethrow; rethrow;
} finally { } finally {
final DateTime endTime = clock.now(); final DateTime endTime = systemClock.now();
printTrace('"flutter $name" took ${getElapsedAsMilliseconds(endTime.difference(startTime))}.'); printTrace('"flutter $name" took ${getElapsedAsMilliseconds(endTime.difference(startTime))}.');
// This is checking the result of the call to 'usagePath' // This is checking the result of the call to 'usagePath'
// (a Future<String>), and not the result of evaluating the Future. // (a Future<String>), and not the result of evaluating the Future.
......
...@@ -6,7 +6,6 @@ import 'dart:async'; ...@@ -6,7 +6,6 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:quiver/time.dart';
import 'base/common.dart'; import 'base/common.dart';
import 'base/context.dart'; import 'base/context.dart';
...@@ -14,12 +13,13 @@ import 'base/file_system.dart'; ...@@ -14,12 +13,13 @@ import 'base/file_system.dart';
import 'base/io.dart'; import 'base/io.dart';
import 'base/process.dart'; import 'base/process.dart';
import 'base/process_manager.dart'; import 'base/process_manager.dart';
import 'base/time.dart';
import 'cache.dart'; import 'cache.dart';
import 'globals.dart'; import 'globals.dart';
class FlutterVersion { class FlutterVersion {
@visibleForTesting @visibleForTesting
FlutterVersion([this._clock = const Clock()]) { FlutterVersion([this._clock = const SystemClock()]) {
_channel = _runGit('git rev-parse --abbrev-ref --symbolic @{u}'); _channel = _runGit('git rev-parse --abbrev-ref --symbolic @{u}');
final String branch = _runGit('git rev-parse --abbrev-ref HEAD'); final String branch = _runGit('git rev-parse --abbrev-ref HEAD');
_branch = branch == 'HEAD' ? _channel : branch; _branch = branch == 'HEAD' ? _channel : branch;
...@@ -38,7 +38,7 @@ class FlutterVersion { ...@@ -38,7 +38,7 @@ class FlutterVersion {
_frameworkVersion = GitTagVersion.determine().frameworkVersionFor(_frameworkRevision); _frameworkVersion = GitTagVersion.determine().frameworkVersionFor(_frameworkRevision);
} }
final Clock _clock; final SystemClock _clock;
String _repositoryUrl; String _repositoryUrl;
String get repositoryUrl => _repositoryUrl; String get repositoryUrl => _repositoryUrl;
...@@ -273,7 +273,7 @@ class FlutterVersion { ...@@ -273,7 +273,7 @@ class FlutterVersion {
// Do not load the stamp before the above server check as it may modify the stamp file. // Do not load the stamp before the above server check as it may modify the stamp file.
final VersionCheckStamp stamp = await VersionCheckStamp.load(); final VersionCheckStamp stamp = await VersionCheckStamp.load();
final DateTime lastTimeWarningWasPrinted = stamp.lastTimeWarningWasPrinted ?? _clock.agoBy(kMaxTimeSinceLastWarning * 2); final DateTime lastTimeWarningWasPrinted = stamp.lastTimeWarningWasPrinted ?? _clock.ago(kMaxTimeSinceLastWarning * 2);
final bool beenAWhileSinceWarningWasPrinted = _clock.now().difference(lastTimeWarningWasPrinted) > kMaxTimeSinceLastWarning; final bool beenAWhileSinceWarningWasPrinted = _clock.now().difference(lastTimeWarningWasPrinted) > kMaxTimeSinceLastWarning;
// We show a warning if either we know there is a new remote version, or we couldn't tell but the local // We show a warning if either we know there is a new remote version, or we couldn't tell but the local
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:quiver/time.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
...@@ -56,7 +56,7 @@ void main() { ...@@ -56,7 +56,7 @@ void main() {
await runner.run(<String>['doctor']); await runner.run(<String>['doctor']);
expect(count, 0); expect(count, 0);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FlutterVersion: () => FlutterVersion(const Clock()), FlutterVersion: () => FlutterVersion(const SystemClock()),
Usage: () => Usage(configDirOverride: tempDir.path), Usage: () => Usage(configDirOverride: tempDir.path),
}); });
...@@ -75,14 +75,14 @@ void main() { ...@@ -75,14 +75,14 @@ void main() {
await runner.run(<String>['config']); await runner.run(<String>['config']);
expect(count, 0); expect(count, 0);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FlutterVersion: () => FlutterVersion(const Clock()), FlutterVersion: () => FlutterVersion(const SystemClock()),
Usage: () => Usage(configDirOverride: tempDir.path), Usage: () => Usage(configDirOverride: tempDir.path),
}); });
}); });
group('analytics with mocks', () { group('analytics with mocks', () {
Usage mockUsage; Usage mockUsage;
Clock mockClock; SystemClock mockClock;
Doctor mockDoctor; Doctor mockDoctor;
List<int> mockTimes; List<int> mockTimes;
...@@ -110,7 +110,7 @@ void main() { ...@@ -110,7 +110,7 @@ void main() {
<dynamic>['flutter', 'doctor', const Duration(milliseconds: 1000), 'success'] <dynamic>['flutter', 'doctor', const Duration(milliseconds: 1000), 'success']
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Clock: () => mockClock, SystemClock: () => mockClock,
Doctor: () => mockDoctor, Doctor: () => mockDoctor,
Usage: () => mockUsage, Usage: () => mockUsage,
}); });
...@@ -129,7 +129,7 @@ void main() { ...@@ -129,7 +129,7 @@ void main() {
<dynamic>['flutter', 'doctor', const Duration(milliseconds: 1000), 'warning'] <dynamic>['flutter', 'doctor', const Duration(milliseconds: 1000), 'warning']
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Clock: () => mockClock, SystemClock: () => mockClock,
Doctor: () => mockDoctor, Doctor: () => mockDoctor,
Usage: () => mockUsage, Usage: () => mockUsage,
}); });
......
...@@ -2,7 +2,16 @@ ...@@ -2,7 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_device.dart'; import 'package:flutter_tools/src/fuchsia/fuchsia_device.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
...@@ -34,5 +43,102 @@ d 2 0 . ...@@ -34,5 +43,102 @@ d 2 0 .
expect(ports.length, 1); expect(ports.length, 1);
expect(ports.single, 36780); expect(ports.single, 36780);
}); });
group('device logs', () {
const String exampleUtcLogs = '''
[2018-11-09 01:27:45][3][297950920][log] INFO: example_app(flutter): Error doing thing
[2018-11-09 01:27:58][46257][46269][foo] INFO: Using a thing
[2018-11-09 01:29:58][46257][46269][foo] INFO: Blah blah blah
[2018-11-09 01:29:58][46257][46269][foo] INFO: other_app(flutter): Do thing
[2018-11-09 01:30:02][41175][41187][bar] INFO: Invoking a bar
[2018-11-09 01:30:12][52580][52983][log] INFO: example_app(flutter): Did thing this time
''';
final MockProcessManager mockProcessManager = MockProcessManager();
final MockProcess mockProcess = MockProcess();
Completer<int> exitCode;
StreamController<List<int>> stdout;
StreamController<List<int>> stderr;
when(mockProcessManager.start(any)).thenAnswer((Invocation _) => Future<Process>.value(mockProcess));
when(mockProcess.exitCode).thenAnswer((Invocation _) => exitCode.future);
when(mockProcess.stdout).thenAnswer((Invocation _) => stdout.stream);
when(mockProcess.stderr).thenAnswer((Invocation _) => stderr.stream);
setUp(() {
stdout = StreamController<List<int>>(sync: true);
stderr = StreamController<List<int>>(sync: true);
exitCode = Completer<int>();
});
tearDown(() {
exitCode.complete(0);
});
testUsingContext('can be parsed for an app', () async {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
final DeviceLogReader reader = device.getLogReader(app: FuchsiaModulePackage(name: 'example_app'));
final List<String> logLines = <String>[];
reader.logLines.listen(logLines.add);
expect(logLines, isEmpty);
stdout.add(utf8.encode(exampleUtcLogs));
await stdout.close();
expect(logLines, <String>[
'[2018-11-09 01:27:45.000] Flutter: Error doing thing',
'[2018-11-09 01:30:12.000] Flutter: Did thing this time',
]);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)),
});
testUsingContext('cuts off prior logs', () async {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
final DeviceLogReader reader = device.getLogReader(app: FuchsiaModulePackage(name: 'example_app'));
final List<String> logLines = <String>[];
final Completer<void> lock = Completer<void>();
reader.logLines.listen((String line) {
logLines.add(line);
lock.complete();
});
expect(logLines, isEmpty);
stdout.add(utf8.encode(exampleUtcLogs));
await stdout.close();
await lock.future;
expect(logLines, <String>[
'[2018-11-09 01:30:12.000] Flutter: Did thing this time',
]);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 29, 45)),
});
testUsingContext('can be parsed for all apps', () async {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
final DeviceLogReader reader = device.getLogReader();
final List<String> logLines = <String>[];
reader.logLines.listen(logLines.add);
expect(logLines, isEmpty);
stdout.add(utf8.encode(exampleUtcLogs));
await stdout.close();
expect(logLines, <String>[
'[2018-11-09 01:27:45.000] Flutter: Error doing thing',
'[2018-11-09 01:29:58.000] Flutter: Do thing',
'[2018-11-09 01:30:12.000] Flutter: Did thing this time',
]);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)),
});
});
}); });
} }
class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {}
...@@ -5,15 +5,14 @@ ...@@ -5,15 +5,14 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/usage.dart'; import 'package:flutter_tools/src/usage.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:quiver/time.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
void main() { void main() {
group('Flutter Command', () { group('Flutter Command', () {
...@@ -67,7 +66,7 @@ void main() { ...@@ -67,7 +66,7 @@ void main() {
); );
}, },
overrides: <Type, Generator>{ overrides: <Type, Generator>{
Clock: () => clock, SystemClock: () => clock,
Usage: () => usage, Usage: () => usage,
}); });
...@@ -84,7 +83,7 @@ void main() { ...@@ -84,7 +83,7 @@ void main() {
label: anyNamed('label'))); label: anyNamed('label')));
}, },
overrides: <Type, Generator>{ overrides: <Type, Generator>{
Clock: () => clock, SystemClock: () => clock,
Usage: () => usage, Usage: () => usage,
}); });
...@@ -117,7 +116,7 @@ void main() { ...@@ -117,7 +116,7 @@ void main() {
); );
}, },
overrides: <Type, Generator>{ overrides: <Type, Generator>{
Clock: () => clock, SystemClock: () => clock,
Usage: () => usage, Usage: () => usage,
}); });
...@@ -153,7 +152,7 @@ void main() { ...@@ -153,7 +152,7 @@ void main() {
} }
}, },
overrides: <Type, Generator>{ overrides: <Type, Generator>{
Clock: () => clock, SystemClock: () => clock,
Usage: () => usage, Usage: () => usage,
}); });
......
...@@ -19,11 +19,11 @@ import 'package:flutter_tools/src/device.dart'; ...@@ -19,11 +19,11 @@ import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/doctor.dart'; import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/ios/simulators.dart'; import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/usage.dart'; import 'package:flutter_tools/src/usage.dart';
import 'package:flutter_tools/src/version.dart'; import 'package:flutter_tools/src/version.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:quiver/time.dart';
import 'common.dart'; import 'common.dart';
...@@ -300,7 +300,7 @@ class MockXcodeProjectInterpreter implements XcodeProjectInterpreter { ...@@ -300,7 +300,7 @@ class MockXcodeProjectInterpreter implements XcodeProjectInterpreter {
class MockFlutterVersion extends Mock implements FlutterVersion {} class MockFlutterVersion extends Mock implements FlutterVersion {}
class MockClock extends Mock implements Clock {} class MockClock extends Mock implements SystemClock {}
class MockHttpClient extends Mock implements HttpClient {} class MockHttpClient extends Mock implements HttpClient {}
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_tools/src/base/time.dart';
import 'src/common.dart';
void main() {
group(SystemClock, () {
test('can set a fixed time', () {
final SystemClock clock = SystemClock.fixed(DateTime(1991, 8, 23));
expect(clock.now(), DateTime(1991, 8, 23));
});
test('can find a time ago', () {
final SystemClock clock = SystemClock.fixed(DateTime(1991, 8, 23));
expect(clock.ago(Duration(days: 10)), DateTime(1991, 8, 13));
});
});
}
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
import 'dart:convert'; import 'dart:convert';
import 'package:collection/collection.dart' show ListEquality; import 'package:collection/collection.dart' show ListEquality;
import 'package:flutter_tools/src/base/time.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'package:quiver/time.dart';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
...@@ -18,11 +18,11 @@ import 'package:flutter_tools/src/version.dart'; ...@@ -18,11 +18,11 @@ import 'package:flutter_tools/src/version.dart';
import 'src/common.dart'; import 'src/common.dart';
import 'src/context.dart'; import 'src/context.dart';
final Clock _testClock = Clock.fixed(DateTime(2015, 1, 1)); final SystemClock _testClock = SystemClock.fixed(DateTime(2015, 1, 1));
final DateTime _upToDateVersion = _testClock.agoBy(FlutterVersion.kVersionAgeConsideredUpToDate ~/ 2); final DateTime _upToDateVersion = _testClock.ago(FlutterVersion.kVersionAgeConsideredUpToDate ~/ 2);
final DateTime _outOfDateVersion = _testClock.agoBy(FlutterVersion.kVersionAgeConsideredUpToDate * 2); final DateTime _outOfDateVersion = _testClock.ago(FlutterVersion.kVersionAgeConsideredUpToDate * 2);
final DateTime _stampUpToDate = _testClock.agoBy(FlutterVersion.kCheckAgeConsideredUpToDate ~/ 2); final DateTime _stampUpToDate = _testClock.ago(FlutterVersion.kCheckAgeConsideredUpToDate ~/ 2);
final DateTime _stampOutOfDate = _testClock.agoBy(FlutterVersion.kCheckAgeConsideredUpToDate * 2); final DateTime _stampOutOfDate = _testClock.ago(FlutterVersion.kCheckAgeConsideredUpToDate * 2);
void main() { void main() {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
...@@ -161,7 +161,7 @@ void main() { ...@@ -161,7 +161,7 @@ void main() {
localCommitDate: _outOfDateVersion, localCommitDate: _outOfDateVersion,
stamp: VersionCheckStamp( stamp: VersionCheckStamp(
lastTimeVersionWasChecked: _stampOutOfDate, lastTimeVersionWasChecked: _stampOutOfDate,
lastKnownRemoteVersion: _testClock.ago(days: 2), lastKnownRemoteVersion: _testClock.ago(const Duration(days: 2)),
), ),
remoteCommitDate: _upToDateVersion, remoteCommitDate: _upToDateVersion,
expectSetStamp: true, expectSetStamp: true,
...@@ -287,15 +287,15 @@ void main() { ...@@ -287,15 +287,15 @@ void main() {
testUsingContext('loads valid JSON', () async { testUsingContext('loads valid JSON', () async {
fakeData(mockProcessManager, mockCache, stampJson: ''' fakeData(mockProcessManager, mockCache, stampJson: '''
{ {
"lastKnownRemoteVersion": "${_testClock.ago(days: 1)}", "lastKnownRemoteVersion": "${_testClock.ago(const Duration(days: 1))}",
"lastTimeVersionWasChecked": "${_testClock.ago(days: 2)}", "lastTimeVersionWasChecked": "${_testClock.ago(const Duration(days: 2))}",
"lastTimeWarningWasPrinted": "${_testClock.now()}" "lastTimeWarningWasPrinted": "${_testClock.now()}"
} }
'''); ''');
final VersionCheckStamp stamp = await VersionCheckStamp.load(); final VersionCheckStamp stamp = await VersionCheckStamp.load();
expect(stamp.lastKnownRemoteVersion, _testClock.ago(days: 1)); expect(stamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1)));
expect(stamp.lastTimeVersionWasChecked, _testClock.ago(days: 2)); expect(stamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2)));
expect(stamp.lastTimeWarningWasPrinted, _testClock.now()); expect(stamp.lastTimeWarningWasPrinted, _testClock.now());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FlutterVersion: () => FlutterVersion(_testClock), FlutterVersion: () => FlutterVersion(_testClock),
...@@ -309,15 +309,15 @@ void main() { ...@@ -309,15 +309,15 @@ void main() {
_expectDefault(await VersionCheckStamp.load()); _expectDefault(await VersionCheckStamp.load());
final VersionCheckStamp stamp = VersionCheckStamp( final VersionCheckStamp stamp = VersionCheckStamp(
lastKnownRemoteVersion: _testClock.ago(days: 1), lastKnownRemoteVersion: _testClock.ago(const Duration(days: 1)),
lastTimeVersionWasChecked: _testClock.ago(days: 2), lastTimeVersionWasChecked: _testClock.ago(const Duration(days: 2)),
lastTimeWarningWasPrinted: _testClock.now(), lastTimeWarningWasPrinted: _testClock.now(),
); );
await stamp.store(); await stamp.store();
final VersionCheckStamp storedStamp = await VersionCheckStamp.load(); final VersionCheckStamp storedStamp = await VersionCheckStamp.load();
expect(storedStamp.lastKnownRemoteVersion, _testClock.ago(days: 1)); expect(storedStamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1)));
expect(storedStamp.lastTimeVersionWasChecked, _testClock.ago(days: 2)); expect(storedStamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2)));
expect(storedStamp.lastTimeWarningWasPrinted, _testClock.now()); expect(storedStamp.lastTimeWarningWasPrinted, _testClock.now());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FlutterVersion: () => FlutterVersion(_testClock), FlutterVersion: () => FlutterVersion(_testClock),
...@@ -331,19 +331,19 @@ void main() { ...@@ -331,19 +331,19 @@ void main() {
_expectDefault(await VersionCheckStamp.load()); _expectDefault(await VersionCheckStamp.load());
final VersionCheckStamp stamp = VersionCheckStamp( final VersionCheckStamp stamp = VersionCheckStamp(
lastKnownRemoteVersion: _testClock.ago(days: 10), lastKnownRemoteVersion: _testClock.ago(const Duration(days: 10)),
lastTimeVersionWasChecked: _testClock.ago(days: 9), lastTimeVersionWasChecked: _testClock.ago(const Duration(days: 9)),
lastTimeWarningWasPrinted: _testClock.ago(days: 8), lastTimeWarningWasPrinted: _testClock.ago(const Duration(days: 8)),
); );
await stamp.store( await stamp.store(
newKnownRemoteVersion: _testClock.ago(days: 1), newKnownRemoteVersion: _testClock.ago(const Duration(days: 1)),
newTimeVersionWasChecked: _testClock.ago(days: 2), newTimeVersionWasChecked: _testClock.ago(const Duration(days: 2)),
newTimeWarningWasPrinted: _testClock.now(), newTimeWarningWasPrinted: _testClock.now(),
); );
final VersionCheckStamp storedStamp = await VersionCheckStamp.load(); final VersionCheckStamp storedStamp = await VersionCheckStamp.load();
expect(storedStamp.lastKnownRemoteVersion, _testClock.ago(days: 1)); expect(storedStamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1)));
expect(storedStamp.lastTimeVersionWasChecked, _testClock.ago(days: 2)); expect(storedStamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2)));
expect(storedStamp.lastTimeWarningWasPrinted, _testClock.now()); expect(storedStamp.lastTimeWarningWasPrinted, _testClock.now());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FlutterVersion: () => FlutterVersion(_testClock), FlutterVersion: () => FlutterVersion(_testClock),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment