diff --git a/agent/analysis_options.yaml b/agent/analysis_options.yaml new file mode 100644 index 0000000000..4e654ad261 --- /dev/null +++ b/agent/analysis_options.yaml @@ -0,0 +1,72 @@ +analyzer: + language: + enableStrictCallChecks: true + enableSuperMixins: true + strong-mode: true + errors: + # allow overriding fields (if they use super, ideally...) + strong_mode_invalid_field_override: ignore + # allow type narrowing + strong_mode_invalid_method_override: ignore + strong_mode_static_type_error: ignore + strong_mode_down_cast_composite: ignore + # allow having TODOs in the code + todo: ignore + exclude: + - 'bin/**' + +linter: + rules: + # these rules are documented on and in the same order as + # the Dart Lint rules page to make maintenance easier + # http://dart-lang.github.io/linter/lints/ + + # === error rules === + - avoid_empty_else + # - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153 + - cancel_subscriptions + # - close_sinks # https://github.com/flutter/flutter/issues/5789 + - control_flow_in_finally + - empty_statements + - hash_and_equals + # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 + # - iterable_contains_unrelated_type + # - list_remove_unrelated_type + # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 + - test_types_in_equals + - throw_in_finally + - unrelated_type_equality_checks + - valid_regexps + + # === style rules === + - always_declare_return_types + - annotate_overrides + - avoid_init_to_null + - avoid_return_types_on_setters + - await_only_futures + - camel_case_types + # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204 + - control_flow_in_finally + - empty_constructor_bodies + - implementation_imports + - library_names + - library_prefixes + - non_constant_identifier_names + - one_member_abstracts + # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 + # - overridden_fields + - package_api_docs + - package_prefixed_library_names + - prefer_is_not_empty + # - public_member_api_docs + - slash_for_doc_comments + - sort_constructors_first + - sort_unnamed_constructors_first + - super_goes_last + # - type_annotate_public_apis # subset of always_specify_types + - type_init_formals + - unawaited_futures # https://github.com/flutter/flutter/issues/5793 + - unnecessary_getters_setters + + # === pub rules === + - package_names diff --git a/agent/bin/agent.dart b/agent/bin/agent.dart index 2bfe12f723..c405a0b374 100644 --- a/agent/bin/agent.dart +++ b/agent/bin/agent.dart @@ -64,6 +64,7 @@ class AuthenticatedClient extends BaseClient { final String _authToken; final Client _delegate = new Client(); + @override Future send(Request request) async { request.headers['Agent-ID'] = _agentId; request.headers['Agent-Auth-Token'] = _authToken; diff --git a/agent/lib/src/adb.dart b/agent/lib/src/adb.dart index 0bc4be27b6..c8a0e34700 100644 --- a/agent/lib/src/adb.dart +++ b/agent/lib/src/adb.dart @@ -138,7 +138,7 @@ class AndroidDeviceDiscovery implements DeviceDiscovery { } class AndroidDevice implements Device { - AndroidDevice({@required String this.deviceId}); + AndroidDevice({@required this.deviceId}); @override final String deviceId; @@ -248,7 +248,7 @@ class IosDeviceDiscovery implements DeviceDiscovery { /// iOS device. class IosDevice implements Device { - const IosDevice({ @required String this.deviceId }); + const IosDevice({ @required this.deviceId }); @override final String deviceId; @@ -258,10 +258,21 @@ class IosDevice implements Device { // devices are already unlocked. For now we'll just keep them at minimum // screen brightness so they don't drain battery too fast. + @override Future isAwake() async => true; + + @override Future isAsleep() async => false; + + @override Future wakeUp() async {} + + @override Future sendToSleep() async {} + + @override Future togglePower() async {} + + @override Future unlock() async {} } diff --git a/agent/lib/src/agent.dart b/agent/lib/src/agent.dart index bed53cff39..e4d605626b 100644 --- a/agent/lib/src/agent.dart +++ b/agent/lib/src/agent.dart @@ -129,6 +129,7 @@ class AuthenticatedClient extends BaseClient { final String _authToken; final Client _delegate = new Client(); + @override Future send(Request request) async { request.headers['Agent-ID'] = _agentId; request.headers['Agent-Auth-Token'] = _authToken; @@ -162,7 +163,7 @@ class AgentHealth { bool get ok => checks.isNotEmpty && checks.values.every((HealthCheckResult r) => r.succeeded); /// Sets a health check [result] for a given [parameter]. - operator []=(String parameter, HealthCheckResult result) { + void operator []=(String parameter, HealthCheckResult result) { if (checks.containsKey(parameter)) { print('WARNING: duplicate health check ${parameter} submitted'); } diff --git a/agent/lib/src/runner.dart b/agent/lib/src/runner.dart index bf7e9dca0e..6a64c7af41 100644 --- a/agent/lib/src/runner.dart +++ b/agent/lib/src/runner.dart @@ -89,6 +89,7 @@ Future runTask(Agent agent, CocoonTask task) async { bool runnerFinished = false; + // ignore: unawaited_futures runner.exitCode.then((_) { runnerFinished = true; }); @@ -126,7 +127,7 @@ Future runTask(Agent agent, CocoonTask task) async { } finally { await stdoutSub.cancel(); await stderrSub.cancel(); - sendLog('Task execution finished', flush: true); + await sendLog('Task execution finished', flush: true); if (!runnerFinished) runner.kill(ProcessSignal.SIGKILL); await forceQuitRunningProcesses(); diff --git a/agent/lib/src/utils.dart b/agent/lib/src/utils.dart index 9ef3e0e0ba..e086dd471c 100644 --- a/agent/lib/src/utils.dart +++ b/agent/lib/src/utils.dart @@ -165,6 +165,7 @@ Future startProcess(String executable, List arguments, ProcessInfo procInfo = new ProcessInfo(command, proc); _runningProcesses.add(procInfo); + // ignore: unawaited_futures proc.exitCode.then((_) { _runningProcesses.remove(procInfo); }); @@ -351,7 +352,7 @@ String requireEnvVar(String name) { return value; } -dynamic/*=T*/ requireConfigProperty(Map*/> map, String propertyName) { +dynamic/*=T*/ requireConfigProperty/**/(Map*/> map, String propertyName) { if (!map.containsKey(propertyName)) fail('Configuration property not found: $propertyName'); diff --git a/agent/test/src/adb_test.dart b/agent/test/src/adb_test.dart index 6859bf6fe4..9bba37c887 100644 --- a/agent/test/src/adb_test.dart +++ b/agent/test/src/adb_test.dart @@ -119,7 +119,7 @@ class CommandArgs { String toString() => 'CommandArgs(command: $command, arguments: $arguments, env: $env)'; @override - operator==(Object other) { + bool operator==(Object other) { if (other.runtimeType != CommandArgs) return false; diff --git a/agent/test/src/utils_test.dart b/agent/test/src/utils_test.dart index 18b1916623..7b9688fa88 100644 --- a/agent/test/src/utils_test.dart +++ b/agent/test/src/utils_test.dart @@ -5,7 +5,7 @@ import 'package:test/test.dart'; import 'package:cocoon_agent/src/utils.dart'; -main() { +void main() { group('grep', () { test('greps lines', () { expect(grep('b', from: 'ab\ncd\nba'), ['ab', 'ba']); diff --git a/app/analysis_options.yaml b/app/analysis_options.yaml new file mode 100644 index 0000000000..4e654ad261 --- /dev/null +++ b/app/analysis_options.yaml @@ -0,0 +1,72 @@ +analyzer: + language: + enableStrictCallChecks: true + enableSuperMixins: true + strong-mode: true + errors: + # allow overriding fields (if they use super, ideally...) + strong_mode_invalid_field_override: ignore + # allow type narrowing + strong_mode_invalid_method_override: ignore + strong_mode_static_type_error: ignore + strong_mode_down_cast_composite: ignore + # allow having TODOs in the code + todo: ignore + exclude: + - 'bin/**' + +linter: + rules: + # these rules are documented on and in the same order as + # the Dart Lint rules page to make maintenance easier + # http://dart-lang.github.io/linter/lints/ + + # === error rules === + - avoid_empty_else + # - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153 + - cancel_subscriptions + # - close_sinks # https://github.com/flutter/flutter/issues/5789 + - control_flow_in_finally + - empty_statements + - hash_and_equals + # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 + # - iterable_contains_unrelated_type + # - list_remove_unrelated_type + # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 + - test_types_in_equals + - throw_in_finally + - unrelated_type_equality_checks + - valid_regexps + + # === style rules === + - always_declare_return_types + - annotate_overrides + - avoid_init_to_null + - avoid_return_types_on_setters + - await_only_futures + - camel_case_types + # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204 + - control_flow_in_finally + - empty_constructor_bodies + - implementation_imports + - library_names + - library_prefixes + - non_constant_identifier_names + - one_member_abstracts + # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 + # - overridden_fields + - package_api_docs + - package_prefixed_library_names + - prefer_is_not_empty + # - public_member_api_docs + - slash_for_doc_comments + - sort_constructors_first + - sort_unnamed_constructors_first + - super_goes_last + # - type_annotate_public_apis # subset of always_specify_types + - type_init_formals + - unawaited_futures # https://github.com/flutter/flutter/issues/5793 + - unnecessary_getters_setters + + # === pub rules === + - package_names diff --git a/app/bin/dev_server.dart b/app/bin/dev_server.dart index 4ecb54162c..8aa3df74dc 100644 --- a/app/bin/dev_server.dart +++ b/app/bin/dev_server.dart @@ -28,7 +28,7 @@ const _pubServePort = 9091; /// Runs `pub serve` and `goapp serve` such that the app can be debugged in /// Dartium. -main(List rawArgs) { +void main(List rawArgs) { ArgResults args = _parseArgs(rawArgs); Zone.current.fork(specification: new ZoneSpecification(handleUncaughtError: _handleCatastrophy)) @@ -49,7 +49,7 @@ ArgResults _parseArgs(List rawArgs) { return argp.parse(rawArgs); } -_start(ArgResults args) async { +Future _start(ArgResults args) async { bool clearDatastore = args['clear-datastore']; _streamSubscriptions.addAll([ @@ -89,7 +89,7 @@ _start(ArgResults args) async { await _whenLocalPortIsListening(_pubServePort); } catch(_) { print('\n[ERROR] Timed out waiting for goapp and pub ports to become available\n'); - _stop(); + await _stop(); } HttpClient http = new HttpClient(); @@ -101,7 +101,7 @@ _start(ArgResults args) async { print('Failed redirecting ${request.uri}'); print(e); print(s); - _stop(); + await _stop(); } } } @@ -212,6 +212,7 @@ Future _stop([ProcessSignal signal = ProcessSignal.SIGINT]) async { _streamSubscriptions.forEach((s) => s.cancel()); await devServer.close(force: true); + // ignore: unawaited_futures Future .wait(_childProcesses.map((p) => p.process.exitCode)) .timeout(const Duration(seconds: 5)) diff --git a/app/lib/components/status_table.dart b/app/lib/components/status_table.dart index 324bbddf30..0ce74f240d 100644 --- a/app/lib/components/status_table.dart +++ b/app/lib/components/status_table.dart @@ -98,8 +98,8 @@ class StatusTable implements OnInit { List agentStatuses; @override - ngOnInit() async { - reloadData(); + Future ngOnInit() async { + await reloadData(); new Timer.periodic(const Duration(seconds: 30), (_) => reloadData()); } diff --git a/app/lib/entity.dart b/app/lib/entity.dart index f07a9067e3..826ccedb5c 100644 --- a/app/lib/entity.dart +++ b/app/lib/entity.dart @@ -14,8 +14,8 @@ class Entity { final EntitySerializer _entitySerializer; final Map _props; - operator[](String propName) => _props[propName]; - operator[]=(String propName, dynamic value) { + dynamic operator[](String propName) => _props[propName]; + void operator[]=(String propName, dynamic value) { _props[propName] = value; } @@ -53,6 +53,7 @@ class ListSerializer implements JsonSerializer> { final JsonSerializer elementSerializer; + @override List deserialize(dynamic jsonValue) { if (jsonValue == null) return null; @@ -70,6 +71,7 @@ class ListSerializer implements JsonSerializer> { return result; } + @override List serialize(List value) { return value .map((E elem) => elementSerializer.serialize(elem)) @@ -80,27 +82,36 @@ class ListSerializer implements JsonSerializer> { class StringSerializer implements JsonSerializer { const StringSerializer(); + @override String deserialize(dynamic jsonValue) { return jsonValue as String; } + + @override dynamic serialize(String value) => value; } class BoolSerializer implements JsonSerializer { const BoolSerializer(); + @override bool deserialize(dynamic jsonValue) { return jsonValue as bool; } + + @override dynamic serialize(bool value) => value; } class NumSerializer implements JsonSerializer { const NumSerializer(); + @override num deserialize(dynamic jsonValue) { return jsonValue as num; } + + @override dynamic serialize(num value) => value; } @@ -135,6 +146,7 @@ class EntitySerializer implements JsonSerializer { final EntityFactory _entityFactory; final Map _propertyCodecs; + @override T deserialize(dynamic jsonValue) { Map props = {}; (jsonValue as Map).forEach((String propName, dynamic propJsonValue) { @@ -150,6 +162,7 @@ class EntitySerializer implements JsonSerializer { return _entityFactory(props); } + @override dynamic serialize(T value) { Map json = {}; value._props.forEach((String propName, dynamic propValue) { diff --git a/app/lib/ioutil.dart b/app/lib/ioutil.dart index a05411102e..4287e44ff3 100644 --- a/app/lib/ioutil.dart +++ b/app/lib/ioutil.dart @@ -60,10 +60,12 @@ Future startProcess(String executable, List arguments, if (onKill != null) { bool processExited = false; + // ignore: unawaited_futures proc.exitCode.then((_) { processExited = true; }); + // ignore: unawaited_futures onKill.then((_) { if (!processExited) { print('Caught signal to kill process (PID: ${proc.pid}): $executable ${arguments.join(' ')}'); diff --git a/app/lib/model.dart b/app/lib/model.dart index adacde331a..58d6712bfe 100644 --- a/app/lib/model.dart +++ b/app/lib/model.dart @@ -16,16 +16,18 @@ class Key { int get hashCode => value.hashCode; @override - operator ==(Key other) => other != null && other.value == value; + bool operator ==(Key other) => other != null && other.value == value; } class _KeySerializer implements JsonSerializer { const _KeySerializer(); + @override Key deserialize(dynamic jsonValue) { return new Key(jsonValue); } + @override dynamic serialize(Key key) { return key.value; } @@ -40,11 +42,11 @@ class GetStatusResult extends Entity { } ); + GetStatusResult([Map props]) : super(_serializer, props); + static GetStatusResult fromJson(dynamic json) => _serializer.deserialize(json); - GetStatusResult([Map props]) : super(_serializer, props); - List get statuses => this['Statuses']; List get agentStatuses => this['AgentStatuses']; } diff --git a/app/test/entity_test.dart b/app/test/entity_test.dart index ba2cf8c58e..8adbdd8196 100644 --- a/app/test/entity_test.dart +++ b/app/test/entity_test.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; import 'package:cocoon/entity.dart'; -main() { +void main() { group('Entity', () { test('sets and serializes properties', () { TestEntity e = new TestEntity(); diff --git a/app/test/model_test.dart b/app/test/model_test.dart index 67c53e4fc7..9c1ef1242e 100644 --- a/app/test/model_test.dart +++ b/app/test/model_test.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; import 'package:cocoon/model.dart'; -main() { +void main() { group('GetStatusResult', () { test('deserializes from JSON', () { GetStatusResult result = GetStatusResult.fromJson({ diff --git a/app/web/charts.dart b/app/web/charts.dart index e8232dd5d8..d09d1364d4 100644 --- a/app/web/charts.dart +++ b/app/web/charts.dart @@ -126,16 +126,17 @@ class Measurement { '${d.day.toString().padLeft(2, '0')}'; } + @override String toString() => '${time}s (${dateString})'; } -final List _analysisColumnSpecs = [ +final List _analysisColumnSpecs = [ new ChartColumnSpec(label: 'Time', type: ChartColumnSpec.TYPE_TIMESTAMP), new ChartColumnSpec(label: 'flutter_repo', type: ChartColumnSpec.TYPE_NUMBER, formatter: _printDurationValSeconds), new ChartColumnSpec(label: 'mega_gallery', type: ChartColumnSpec.TYPE_NUMBER, formatter: _printDurationValSeconds) ]; -void _updateAnalysisChart([List data = const []]) { +void _updateAnalysisChart([List data = const []]) { if (data == null || data.length < 2) data = _createPlaceholderData([0.0, 0.0]); @@ -162,12 +163,12 @@ void _updateAnalysisChart([List data = const []]) { analysisChartArea.draw(); } -final List _dartdocColumnSpecs = [ +final List _dartdocColumnSpecs = [ new ChartColumnSpec(label: 'Time', type: ChartColumnSpec.TYPE_TIMESTAMP), new ChartColumnSpec(label: 'Burndown', type: ChartColumnSpec.TYPE_NUMBER) ]; -void _updateDartdocChart([List data]) { +void _updateDartdocChart([List data]) { if (data == null || data.length < 2) data = _createPlaceholderData([0]); @@ -194,12 +195,12 @@ void _updateDartdocChart([List data]) { dartdocChartArea.draw(); } -final List _refreshColumnSpecs = [ +final List _refreshColumnSpecs = [ new ChartColumnSpec(label: 'Time', type: ChartColumnSpec.TYPE_TIMESTAMP), new ChartColumnSpec(label: 'Refresh', type: ChartColumnSpec.TYPE_NUMBER, formatter: _printDurationValMillis) ]; -void _updateRefreshChart([List data = const []]) { +void _updateRefreshChart([List data = const []]) { if (data == null || data.length < 2) data = _createPlaceholderData([0]); @@ -229,8 +230,8 @@ void _updateRefreshChart([List data = const []]) { List _createPlaceholderData(List templateItems) { DateTime now = new DateTime.now(); return [ - [now.subtract(new Duration(days: 30)).millisecondsSinceEpoch]..addAll(templateItems), - [now.millisecondsSinceEpoch]..addAll(templateItems), + [now.subtract(new Duration(days: 30)).millisecondsSinceEpoch]..addAll(templateItems), + [now.millisecondsSinceEpoch]..addAll(templateItems), ]; } diff --git a/app/web/main.dart b/app/web/main.dart index ede103dd67..98a6c357a8 100644 --- a/app/web/main.dart +++ b/app/web/main.dart @@ -16,7 +16,7 @@ import 'package:cocoon/logging.dart'; import 'package:cocoon/cli.dart'; @AngularEntrypoint() -main() async { +Future main() async { logger = new HtmlLogger(); http.Client httpClient = await _getAuthenticatedClientOrRedirectToSignIn();