Migrate benchmark/integration/

R=brianwilkerson@google.com

Change-Id: I0dccc13831ec5aed82d33ee52ffeae1ad38c5b98
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/194109
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/benchmark/integration/driver.dart b/pkg/analysis_server/benchmark/integration/driver.dart
index 6e831aa..9f40d74 100644
--- a/pkg/analysis_server/benchmark/integration/driver.dart
+++ b/pkg/analysis_server/benchmark/integration/driver.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:async';
 import 'dart:math' show max, sqrt;
 
@@ -42,13 +40,13 @@
   final Logger logger = Logger('Driver');
 
   /// The diagnostic port for Analysis Server or `null` if none.
-  final int diagnosticPort;
+  final int? diagnosticPort;
 
   /// A flag indicating whether the server is running.
   bool running = false;
 
   @override
-  Server server;
+  late Server server;
 
   /// The results collected while running analysis server.
   final Results results = Results();
@@ -65,7 +63,7 @@
   /// Perform the given operation.
   /// Return a [Future] that completes when the next operation can be performed,
   /// or `null` if the next operation can be performed immediately
-  Future perform(Operation op) {
+  Future<void>? perform(Operation op) {
     return op.perform(this);
   }
 
@@ -75,7 +73,7 @@
   /// normal (non-error) response, the future will be completed with the
   /// 'result' field from the response.  If the server acknowledges the command
   /// with an error response, the future will be completed with an error.
-  Future<Map<String, dynamic>> send(
+  Future<Map<String, Object?>?> send(
       String method, Map<String, dynamic> params) {
     return server.send(method, params);
   }
@@ -206,14 +204,17 @@
     print('');
     print('==================================================================');
     print('');
-    var keys = measurements.keys.toList()..sort();
-    var keyLen = keys.fold(0, (int len, String key) => max(len, key.length));
+    var sortedEntries = measurements.entries.toList();
+    sortedEntries.sort((a, b) => a.key.compareTo(b.key));
+    var keyLen = sortedEntries
+        .map((e) => e.key)
+        .fold(0, (int len, String key) => max(len, key.length));
     _printGroupHeader('Request/Response', keyLen);
     var totalCount = 0;
     var totalErrorCount = 0;
     var totalUnexpectedResultCount = 0;
-    for (var tag in keys) {
-      var m = measurements[tag];
+    for (var entry in sortedEntries) {
+      var m = entry.value;
       if (!m.notification) {
         m.printSummary(keyLen);
         totalCount += m.count;
@@ -225,8 +226,8 @@
         keyLen, totalCount, totalErrorCount, totalUnexpectedResultCount);
     print('');
     _printGroupHeader('Notifications', keyLen);
-    for (var tag in keys) {
-      var m = measurements[tag];
+    for (var entry in sortedEntries) {
+      var m = entry.value;
       if (m.notification) {
         m.printSummary(keyLen);
       }
@@ -252,7 +253,7 @@
   }
 
   void recordUnexpectedResults(String tag) {
-    measurements[tag].recordUnexpectedResults();
+    measurements[tag]!.recordUnexpectedResults();
   }
 
   void _printGroupHeader(String groupName, int keyLen) {
diff --git a/pkg/analysis_server/benchmark/integration/input_converter.dart b/pkg/analysis_server/benchmark/integration/input_converter.dart
index e128456..0f86919 100644
--- a/pkg/analysis_server/benchmark/integration/input_converter.dart
+++ b/pkg/analysis_server/benchmark/integration/input_converter.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
@@ -20,14 +18,14 @@
 import 'operation.dart';
 
 /// Common input converter superclass for sharing implementation.
-abstract class CommonInputConverter extends Converter<String, Operation> {
+abstract class CommonInputConverter extends Converter<String, Operation?> {
   static final ERROR_PREFIX = 'Server responded with an error: ';
   final Logger logger = Logger('InstrumentationInputConverter');
   final Set<String> eventsSeen = <String>{};
 
   /// A mapping from request/response id to request json
   /// for those requests for which a response has not been processed.
-  final Map<String, dynamic> requestMap = {};
+  final Map<String, Object?> requestMap = {};
 
   /// A mapping from request/response id to a completer
   /// for those requests for which a response has not been processed.
@@ -37,7 +35,7 @@
 
   /// A mapping from request/response id to the actual response result
   /// for those responses that have not been processed.
-  final Map<String, dynamic> responseMap = {};
+  final Map<String, Object?> responseMap = {};
 
   /// A mapping of current overlay content
   /// parallel to what is in the analysis server
@@ -58,16 +56,18 @@
 
   CommonInputConverter(this.tmpSrcDirPath, this.srcPathMap);
 
-  Map<String, dynamic> asMap(dynamic value) => value as Map<String, dynamic>;
+  Map<String, Object?> asMap(dynamic value) => value as Map<String, Object?>;
+
+  Map<String, Object?>? asMap2(dynamic value) => value as Map<String, Object?>?;
 
   /// Return an operation for the notification or `null` if none.
-  Operation convertNotification(Map<String, dynamic> json) {
+  Operation? convertNotification(Map<String, dynamic> json) {
     String event = json['event'];
     if (event == SERVER_NOTIFICATION_STATUS) {
       // {"event":"server.status","params":{"analysis":{"isAnalyzing":false}}}
-      var params = asMap(json['params']);
+      var params = asMap2(json['params']);
       if (params != null) {
-        var analysis = asMap(params['analysis']);
+        var analysis = asMap2(params['analysis']);
         if (analysis != null && analysis['isAnalyzing'] == false) {
           return WaitForAnalysisCompleteOperation();
         }
@@ -84,23 +84,20 @@
   }
 
   /// Return an operation for the request or `null` if none.
-  Operation convertRequest(Map<String, dynamic> origJson) {
+  Operation convertRequest(Map<String, Object?> origJson) {
     var json = asMap(translateSrcPaths(origJson));
-    requestMap[json['id']] = json;
-    String method = json['method'];
+    requestMap[json['id'] as String] = json;
+    var method = json['method'] as String;
     // Sanity check operations that modify source
     // to ensure that the operation is on source in temp space
     if (method == ANALYSIS_REQUEST_UPDATE_CONTENT) {
       // Track overlays in parallel with the analysis server
       // so that when an overlay is removed, the file can be updated on disk
-      var request = Request.fromJson(json);
+      var request = Request.fromJson(json)!;
       var params = AnalysisUpdateContentParams.fromRequest(request);
       params.files.forEach((String filePath, change) {
         if (change is AddContentOverlay) {
           var content = change.content;
-          if (content == null) {
-            throw 'expected new overlay content\n$json';
-          }
           overlays[filePath] = content;
         } else if (change is ChangeContentOverlay) {
           var content = overlays[filePath];
@@ -172,8 +169,9 @@
   void processErrorResponse(String id, exception) {
     var result = exception;
     if (exception is UnimplementedError) {
-      if (exception.message.startsWith(ERROR_PREFIX)) {
-        result = json.decode(exception.message.substring(ERROR_PREFIX.length));
+      var message = exception.message;
+      if (message!.startsWith(ERROR_PREFIX)) {
+        result = json.decode(message.substring(ERROR_PREFIX.length));
       }
     }
     processResponseResult(id, result);
@@ -186,7 +184,7 @@
   /// Return a future that completes when the response is received
   /// or `null` if the response has already been received
   /// and the completer completed.
-  Future processExpectedResponse(String id, Completer completer) {
+  Future<void>? processExpectedResponse(String id, Completer completer) {
     if (responseMap.containsKey(id)) {
       logger.log(Level.INFO, 'processing cached response $id');
       completer.complete(responseMap.remove(id));
@@ -228,7 +226,7 @@
       return result;
     }
     if (json is Map) {
-      var result = <String, dynamic>{};
+      var result = <String, Object?>{};
       json.forEach((origKey, value) {
         result[translateSrcPaths(origKey)] = translateSrcPaths(value);
       });
@@ -241,7 +239,7 @@
 /// [InputConverter] converts an input stream
 /// into a series of operations to be sent to the analysis server.
 /// The input stream can be either an instrumentation or log file.
-class InputConverter extends Converter<String, Operation> {
+class InputConverter extends Converter<String, Operation?> {
   final Logger logger = Logger('InputConverter');
 
   /// A mapping of source path prefixes
@@ -259,7 +257,7 @@
 
   /// The underlying converter used to translate lines into operations
   /// or `null` if it has not yet been determined.
-  Converter<String, Operation> converter;
+  Converter<String, Operation?>? converter;
 
   /// [active] is `true` if converting lines to operations
   /// or `false` if an exception has occurred.
@@ -268,10 +266,11 @@
   InputConverter(this.tmpSrcDirPath, this.srcPathMap);
 
   @override
-  Operation convert(String line) {
+  Operation? convert(String line) {
     if (!active) {
       return null;
     }
+    var converter = this.converter;
     if (converter != null) {
       try {
         return converter.convert(line);
@@ -335,8 +334,8 @@
 }
 
 class _InputSink extends ChunkedConversionSink<String> {
-  final Converter<String, Operation> converter;
-  final Sink<Operation> outSink;
+  final Converter<String, Operation?> converter;
+  final Sink<Operation?> outSink;
 
   _InputSink(this.converter, this.outSink);
 
diff --git a/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart b/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
index 2cd8783..037c9b0 100644
--- a/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
+++ b/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:convert';
 
 import 'package:analyzer/exception/exception.dart';
@@ -23,17 +21,18 @@
   /// [readBuffer] holds the contents of the file being read from disk
   /// as recorded in the instrumentation log
   /// or `null` if not converting a "Read" entry.
-  StringBuffer readBuffer;
+  StringBuffer? readBuffer;
 
   InstrumentationInputConverter(String tmpSrcDirPath, PathMap srcPathMap)
       : super(tmpSrcDirPath, srcPathMap);
 
   @override
-  Operation convert(String line) {
+  Operation? convert(String line) {
     List<String> fields;
     try {
       fields = _parseFields(line);
       if (fields.length < 2) {
+        var readBuffer = this.readBuffer;
         if (readBuffer != null) {
           readBuffer.writeln(fields.length == 1 ? fields[0] : '');
           return null;
@@ -78,7 +77,7 @@
     return null;
   }
 
-  Map<String, dynamic> decodeJson(String line, String text) {
+  Map<String, Object?> decodeJson(String line, String text) {
     try {
       return asMap(json.decode(text));
     } catch (e, s) {
diff --git a/pkg/analysis_server/benchmark/integration/local_runner.dart b/pkg/analysis_server/benchmark/integration/local_runner.dart
index e8353b2..f8a987a 100644
--- a/pkg/analysis_server/benchmark/integration/local_runner.dart
+++ b/pkg/analysis_server/benchmark/integration/local_runner.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:io';
 
 import 'package:path/path.dart';
@@ -77,7 +75,7 @@
 }
 
 /// Print help and exit
-void printHelp([String errMsg]) {
+void printHelp([String? errMsg]) {
   if (errMsg != null) {
     print('');
     print('Error: $errMsg');
diff --git a/pkg/analysis_server/benchmark/integration/log_file_input_converter.dart b/pkg/analysis_server/benchmark/integration/log_file_input_converter.dart
index 238a1fa..bd87c70 100644
--- a/pkg/analysis_server/benchmark/integration/log_file_input_converter.dart
+++ b/pkg/analysis_server/benchmark/integration/log_file_input_converter.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:convert';
 
 import 'package:analyzer/exception/exception.dart';
@@ -25,7 +23,7 @@
       : super(tmpSrcDirPath, srcPathMap);
 
   @override
-  Operation convert(String line) {
+  Operation? convert(String line) {
     try {
       var timeStampString = _parseTimeStamp(line);
       var data = line.substring(timeStampString.length);
diff --git a/pkg/analysis_server/benchmark/integration/main.dart b/pkg/analysis_server/benchmark/integration/main.dart
index c1c9911..8f0a438 100644
--- a/pkg/analysis_server/benchmark/integration/main.dart
+++ b/pkg/analysis_server/benchmark/integration/main.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
@@ -26,9 +24,9 @@
 
   var driver = Driver(diagnosticPort: args.diagnosticPort);
   var stream = openInput(args);
-  StreamSubscription<Operation> subscription;
-  subscription = stream.listen((Operation op) {
-    var future = driver.perform(op);
+  late StreamSubscription<Operation?> subscription;
+  subscription = stream.listen((Operation? op) {
+    var future = driver.perform(op!);
     if (future != null) {
       logger.log(Level.FINE, 'pausing operations for ${op.runtimeType}');
       subscription.pause(future.then((_) {
@@ -63,17 +61,15 @@
 const VERBOSE_CMDLINE_OPTION = 'verbose';
 const VERY_VERBOSE_CMDLINE_OPTION = 'vv';
 
-ArgParser _argParser;
+late final ArgParser argParser = () {
+  var argParser = ArgParser();
 
-ArgParser get argParser {
-  _argParser = ArgParser();
-
-  _argParser.addOption(INPUT_CMDLINE_OPTION,
+  argParser.addOption(INPUT_CMDLINE_OPTION,
       abbr: 'i',
       help: '<filePath>\n'
           'The input file specifying how this client should interact with the server.\n'
           'If the input file name is "stdin", then the instructions are read from standard input.');
-  _argParser.addMultiOption(MAP_OPTION,
+  argParser.addMultiOption(MAP_OPTION,
       abbr: 'm',
       splitCommas: false,
       help: '<oldSrcPath>,<newSrcPath>\n'
@@ -82,26 +78,26 @@
           'to the target source directory <newSrcPath> used during performance testing.\n'
           'Multiple mappings can be specified.\n'
           'WARNING: The contents of the target directory will be modified');
-  _argParser.addOption(TMP_SRC_DIR_OPTION,
+  argParser.addOption(TMP_SRC_DIR_OPTION,
       abbr: 't',
       help: '<dirPath>\n'
           'The temporary directory containing source used during performance measurement.\n'
           'WARNING: The contents of the target directory will be modified');
-  _argParser.addOption(DIAGNOSTIC_PORT_OPTION,
+  argParser.addOption(DIAGNOSTIC_PORT_OPTION,
       abbr: 'd',
       help: 'localhost port on which server will provide diagnostic web pages');
-  _argParser.addFlag(VERBOSE_CMDLINE_OPTION,
+  argParser.addFlag(VERBOSE_CMDLINE_OPTION,
       abbr: 'v', help: 'Verbose logging', negatable: false);
-  _argParser.addFlag(VERY_VERBOSE_CMDLINE_OPTION,
+  argParser.addFlag(VERY_VERBOSE_CMDLINE_OPTION,
       help: 'Extra verbose logging', negatable: false);
-  _argParser.addFlag(HELP_CMDLINE_OPTION,
+  argParser.addFlag(HELP_CMDLINE_OPTION,
       abbr: 'h', help: 'Print this help information', negatable: false);
-  return _argParser;
-}
+  return argParser;
+}();
 
 /// Open and return the input stream specifying how this client
 /// should interact with the analysis server.
-Stream<Operation> openInput(PerfArgs args) {
+Stream<Operation?> openInput(PerfArgs args) {
   var logger = Logger('openInput');
   Stream<List<int>> inputRaw;
   if (args.inputPath == 'stdin') {
@@ -137,12 +133,12 @@
 
   var showHelp = args[HELP_CMDLINE_OPTION] || args.rest.isNotEmpty;
 
-  bool isMissing(key) => args[key] == null || args[key].isEmpty;
-
-  perfArgs.inputPath = args[INPUT_CMDLINE_OPTION];
-  if (isMissing(INPUT_CMDLINE_OPTION)) {
+  var inputArg = args[INPUT_CMDLINE_OPTION];
+  if (inputArg is! String || inputArg.isEmpty) {
     print('missing $INPUT_CMDLINE_OPTION argument');
     showHelp = true;
+  } else {
+    perfArgs.inputPath = inputArg;
   }
 
   for (String pair in args[MAP_OPTION]) {
@@ -157,18 +153,20 @@
         }
       }
     }
-    print('must specifiy $MAP_OPTION <oldSrcPath>,<newSrcPath>');
+    print('must specify $MAP_OPTION <oldSrcPath>,<newSrcPath>');
     showHelp = true;
   }
 
-  perfArgs.tmpSrcDirPath = _withTrailingSeparator(args[TMP_SRC_DIR_OPTION]);
-  if (isMissing(TMP_SRC_DIR_OPTION)) {
+  var tmpSrcDirPathArg = args[TMP_SRC_DIR_OPTION];
+  if (tmpSrcDirPathArg is! String || tmpSrcDirPathArg.isEmpty) {
     print('missing $TMP_SRC_DIR_OPTION argument');
     showHelp = true;
+  } else {
+    perfArgs.tmpSrcDirPath = _withTrailingSeparator(tmpSrcDirPathArg);
   }
 
-  String portText = args[DIAGNOSTIC_PORT_OPTION];
-  if (portText != null) {
+  var portText = args[DIAGNOSTIC_PORT_OPTION];
+  if (portText is String) {
     if (int.tryParse(portText) == null) {
       print('invalid $DIAGNOSTIC_PORT_OPTION: $portText');
       showHelp = true;
@@ -202,7 +200,7 @@
 
 /// Ensure that the given path has a trailing separator
 String _withTrailingSeparator(String dirPath) {
-  if (dirPath != null && dirPath.length > 4) {
+  if (dirPath.length > 4) {
     if (!dirPath.endsWith(path.separator)) {
       return '$dirPath${path.separator}';
     }
@@ -215,7 +213,7 @@
   /// The file path of the instrumentation or log file
   /// used to drive performance measurement,
   /// or 'stdin' if this information should be read from standard input.
-  String inputPath;
+  late String inputPath;
 
   /// A mapping from the original source directory
   /// when the instrumentation or log file was generated
@@ -224,8 +222,8 @@
 
   /// The temporary directory containing source used during performance
   /// measurement.
-  String tmpSrcDirPath;
+  late String tmpSrcDirPath;
 
   /// The diagnostic port for Analysis Server or `null` if none.
-  int diagnosticPort;
+  int? diagnosticPort;
 }
diff --git a/pkg/analysis_server/benchmark/integration/operation.dart b/pkg/analysis_server/benchmark/integration/operation.dart
index eb70f44..311e979 100644
--- a/pkg/analysis_server/benchmark/integration/operation.dart
+++ b/pkg/analysis_server/benchmark/integration/operation.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:async';
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
@@ -15,10 +13,10 @@
 /// A [CompletionRequestOperation] tracks response time along with
 /// the first and last completion notifications.
 class CompletionRequestOperation extends RequestOperation {
-  Driver driver;
-  StreamSubscription<CompletionResultsParams> subscription;
-  String notificationId;
-  Stopwatch stopwatch;
+  late Driver driver;
+  late StreamSubscription<CompletionResultsParams> subscription;
+  late String notificationId;
+  late Stopwatch stopwatch;
   bool firstNotification = true;
 
   CompletionRequestOperation(
@@ -26,7 +24,7 @@
       : super(converter, json);
 
   @override
-  Future perform(Driver driver) {
+  Future<void>? perform(Driver driver) {
     this.driver = driver;
     subscription = driver.onCompletionResults.listen(processNotification);
     return super.perform(driver);
@@ -50,8 +48,8 @@
 
   @override
   void processResult(
-      String id, Map<String, dynamic> result, Stopwatch stopwatch) {
-    notificationId = result['id'];
+      String id, Map<String, Object?> result, Stopwatch stopwatch) {
+    notificationId = result['id'] as String;
     this.stopwatch = stopwatch;
     super.processResult(id, result, stopwatch);
   }
@@ -59,7 +57,7 @@
 
 /// An [Operation] represents an action such as sending a request to the server.
 abstract class Operation {
-  Future perform(Driver driver);
+  Future<void>? perform(Driver driver);
 }
 
 /// A [RequestOperation] sends a [JSON] request to the server.
@@ -70,7 +68,7 @@
   RequestOperation(this.converter, this.json);
 
   @override
-  Future perform(Driver driver) {
+  Future<void>? perform(Driver driver) {
     var stopwatch = Stopwatch();
     String originalId = json['id'];
     String method = json['method'];
@@ -85,11 +83,9 @@
           .log(Level.FINE, 'Response received: $method : $elapsed\n  $result');
     }
 
-    driver
-        .send(method, converter.asMap(json['params']))
-        .then((Map<String, dynamic> result) {
+    driver.send(method, converter.asMap(json['params'])).then((result) {
       recordResult(true, result);
-      processResult(originalId, result, stopwatch);
+      processResult(originalId, result!, stopwatch);
     }).catchError((exception) {
       recordResult(false, exception);
       converter.processErrorResponse(originalId, exception);
@@ -98,7 +94,7 @@
   }
 
   void processResult(
-      String id, Map<String, dynamic> result, Stopwatch stopwatch) {
+      String id, Map<String, Object?> result, Stopwatch stopwatch) {
     converter.processResponseResult(id, result);
   }
 }
@@ -107,19 +103,20 @@
 class ResponseOperation extends Operation {
   static final Duration responseTimeout = Duration(seconds: 60);
   final CommonInputConverter converter;
-  final Map<String, dynamic> requestJson;
-  final Map<String, dynamic> responseJson;
+  final Map<String, Object?> requestJson;
+  final Map<String, Object?> responseJson;
   final Completer completer = Completer();
-  Driver driver;
+  late Driver driver;
 
   ResponseOperation(this.converter, this.requestJson, this.responseJson) {
     completer.future.then(_processResult).timeout(responseTimeout);
   }
 
   @override
-  Future perform(Driver driver) {
+  Future<void>? perform(Driver driver) {
     this.driver = driver;
-    return converter.processExpectedResponse(responseJson['id'], completer);
+    var id = responseJson['id'] as String;
+    return converter.processExpectedResponse(id, completer);
   }
 
   bool _equal(expectedResult, actualResult) {
@@ -161,7 +158,8 @@
           'expected result:${format(expectedResult)}\n'
           'expected error:${format(expectedError)}\n'
           'but received:${format(actualResult)}';
-      driver.results.recordUnexpectedResults(requestJson['method']);
+      var method = requestJson['method'] as String;
+      driver.results.recordUnexpectedResults(method);
       converter.logOverlayContent();
       if (expectedError == null) {
         converter.logger.log(Level.SEVERE, message);
@@ -184,13 +182,14 @@
   Future perform(Driver driver) {
     var start = DateTime.now();
     driver.logger.log(Level.FINE, 'waiting for analysis to complete');
-    StreamSubscription<ServerStatusParams> subscription;
-    Timer timer;
+    late StreamSubscription<ServerStatusParams> subscription;
+    late Timer timer;
     var completer = Completer();
     var isAnalyzing = false;
     subscription = driver.onServerStatus.listen((ServerStatusParams params) {
-      if (params.analysis != null) {
-        if (params.analysis.isAnalyzing) {
+      var analysisStatus = params.analysis;
+      if (analysisStatus != null) {
+        if (analysisStatus.isAnalyzing) {
           isAnalyzing = true;
         } else {
           subscription.cancel();
@@ -214,7 +213,7 @@
       }
       // Timeout if no communication received within the last 60 seconds.
       var currentTime = driver.server.currentElapseTime;
-      var lastTime = driver.server.lastCommunicationTime;
+      var lastTime = driver.server.lastCommunicationTime!;
       if (currentTime - lastTime > 60) {
         subscription.cancel();
         timer.cancel();