[null-safety] migrate platform to null-safe dart (#24)

Migrate platform to null-safety. Remove stable travis shard since that does not support null safety
diff --git a/.travis.yml b/.travis.yml
index 83a837c..5722c5b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
 language: dart
 sudo: false
 dart:
-  - stable
   - dev
 install:
   - gem install coveralls-lcov
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e42859a..a5f2c99 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+### 3.0.0-nullsafety
+
+* Migrate package to null-safe dart.
+
 ### 2.2.1
 
 * Add `operatingSystemVersion`
diff --git a/.analysis_options b/analysis_options.yaml
similarity index 88%
rename from .analysis_options
rename to analysis_options.yaml
index 9f8e801..1b14e09 100644
--- a/.analysis_options
+++ b/analysis_options.yaml
@@ -1,8 +1,6 @@
 analyzer:
-  language:
-    enableStrictCallChecks: true
-    enableSuperMixins: true
-  strong-mode: true
+  enable-experiment:
+    - non-nullable
   errors:
     # Allow having TODOs in the code
     todo: ignore
@@ -40,7 +38,6 @@
     - await_only_futures
     - camel_case_types
     - constant_identifier_names
-    - control_flow_in_finally
     - empty_constructor_bodies
     - implementation_imports
     - library_names
@@ -56,11 +53,9 @@
     - slash_for_doc_comments
     - sort_constructors_first
     - sort_unnamed_constructors_first
-    - super_goes_last
     - type_annotate_public_apis
     - type_init_formals
     - unawaited_futures
-    - unnecessary_brace_in_string_interp
     - unnecessary_getters_setters
 
     # === pub rules ===
diff --git a/dev/travis.sh b/dev/travis.sh
index 866b361..722d4d8 100755
--- a/dev/travis.sh
+++ b/dev/travis.sh
@@ -15,7 +15,7 @@
 
 # Make sure we pass the analyzer
 echo "Checking dartanalyzer..."
-fails_analyzer="$(find lib test dev -name "*.dart" | xargs dartanalyzer --options .analysis_options)"
+fails_analyzer="$(find lib test dev -name "*.dart" | xargs dartanalyzer --options analysis_options.yaml)"
 if [[ "$fails_analyzer" == *"[error]"* ]]; then
   echo "FAILED"
   echo "$fails_analyzer"
@@ -27,4 +27,4 @@
 set -e
 
 # Run the tests.
-pub run test
+pub run --enable-experiment=non-nullable test
diff --git a/lib/platform.dart b/lib/platform.dart
index 2770219..8110dd1 100644
--- a/lib/platform.dart
+++ b/lib/platform.dart
@@ -2,6 +2,7 @@
 // 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.10
 /// Core interfaces & classes.
 export 'src/interface/local_platform.dart';
 export 'src/interface/platform.dart';
diff --git a/lib/src/interface/local_platform.dart b/lib/src/interface/local_platform.dart
index 20b4bbe..f0c44bb 100644
--- a/lib/src/interface/local_platform.dart
+++ b/lib/src/interface/local_platform.dart
@@ -2,6 +2,7 @@
 // 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.10
 import 'dart:io' as io show Platform, stdin, stdout;
 
 import 'platform.dart';
@@ -42,10 +43,11 @@
   List<String> get executableArguments => io.Platform.executableArguments;
 
   @override
-  String get packageRoot => io.Platform.packageRoot;
+  String? get packageRoot =>
+      io.Platform.packageRoot; // ignore: deprecated_member_use
 
   @override
-  String get packageConfig => io.Platform.packageConfig;
+  String? get packageConfig => io.Platform.packageConfig;
 
   @override
   String get version => io.Platform.version;
diff --git a/lib/src/interface/platform.dart b/lib/src/interface/platform.dart
index afc527a..ee7d28e 100644
--- a/lib/src/interface/platform.dart
+++ b/lib/src/interface/platform.dart
@@ -2,6 +2,7 @@
 // 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.10
 import 'dart:convert';
 
 /// Provides API parity with the `Platform` class in `dart:io`, but using
@@ -101,14 +102,15 @@
   /// Dart packages are looked up.
   ///
   /// If there is no `--package-root` flag, `null` is returned.
-  String get packageRoot;
+  @deprecated
+  String? get packageRoot;
 
   /// The value of the `--packages` flag passed to the executable
   /// used to run the script in this isolate. This is the configuration which
   /// specifies how Dart packages are looked up.
   ///
   /// If there is no `--packages` flag, `null` is returned.
-  String get packageConfig;
+  String? get packageConfig;
 
   /// The version of the current Dart runtime.
   ///
@@ -139,7 +141,8 @@
       'resolvedExecutable': resolvedExecutable,
       'script': script.toString(),
       'executableArguments': executableArguments,
-      'packageRoot': packageRoot,
+      'packageRoot':
+          packageRoot, // ignore: deprecated_member_use_from_same_package
       'packageConfig': packageConfig,
       'version': version,
       'stdinSupportsAnsi': stdinSupportsAnsi,
diff --git a/lib/src/testing/fake_platform.dart b/lib/src/testing/fake_platform.dart
index 0d868bd..0af10bc 100644
--- a/lib/src/testing/fake_platform.dart
+++ b/lib/src/testing/fake_platform.dart
@@ -2,6 +2,7 @@
 // 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.10
 import 'dart:convert';
 
 import '../interface/platform.dart';
@@ -11,46 +12,60 @@
   /// Creates a new [FakePlatform] with the specified properties.
   ///
   /// Unspecified properties will *not* be assigned default values (they will
-  /// remain `null`).
+  /// remain `null`). If an unset non-null value is read, a [StateError] will
+  /// be thrown instead of returnin `null`.
   FakePlatform({
-    this.numberOfProcessors,
-    this.pathSeparator,
-    this.operatingSystem,
-    this.operatingSystemVersion,
-    this.localHostname,
-    this.environment,
-    this.executable,
-    this.resolvedExecutable,
-    this.script,
-    this.executableArguments,
+    int? numberOfProcessors,
+    String? pathSeparator,
+    String? operatingSystem,
+    String? operatingSystemVersion,
+    String? localHostname,
+    Map<String, String>? environment,
+    String? executable,
+    String? resolvedExecutable,
+    Uri? script,
+    List<String>? executableArguments,
     this.packageRoot,
     this.packageConfig,
-    this.version,
-    this.stdinSupportsAnsi,
-    this.stdoutSupportsAnsi,
-    this.localeName,
-  });
+    String? version,
+    bool? stdinSupportsAnsi,
+    bool? stdoutSupportsAnsi,
+    String? localeName,
+  })  : _numberOfProcessors = numberOfProcessors,
+        _pathSeparator = pathSeparator,
+        _operatingSystem = operatingSystem,
+        _operatingSystemVersion = operatingSystemVersion,
+        _localHostname = localHostname,
+        _environment = environment,
+        _executable = executable,
+        _resolvedExecutable = resolvedExecutable,
+        _script = script,
+        _executableArguments = executableArguments,
+        _version = version,
+        _stdinSupportsAnsi = stdinSupportsAnsi,
+        _stdoutSupportsAnsi = stdoutSupportsAnsi,
+        _localeName = localeName;
 
   /// Creates a new [FakePlatform] with properties whose initial values mirror
   /// the specified [platform].
   FakePlatform.fromPlatform(Platform platform)
-      : numberOfProcessors = platform.numberOfProcessors,
-        pathSeparator = platform.pathSeparator,
-        operatingSystem = platform.operatingSystem,
-        operatingSystemVersion = platform.operatingSystemVersion,
-        localHostname = platform.localHostname,
-        environment = new Map<String, String>.from(platform.environment),
-        executable = platform.executable,
-        resolvedExecutable = platform.resolvedExecutable,
-        script = platform.script,
-        executableArguments =
-            new List<String>.from(platform.executableArguments),
-        packageRoot = platform.packageRoot,
+      : _numberOfProcessors = platform.numberOfProcessors,
+        _pathSeparator = platform.pathSeparator,
+        _operatingSystem = platform.operatingSystem,
+        _operatingSystemVersion = platform.operatingSystemVersion,
+        _localHostname = platform.localHostname,
+        _environment = Map<String, String>.from(platform.environment),
+        _executable = platform.executable,
+        _resolvedExecutable = platform.resolvedExecutable,
+        _script = platform.script,
+        _executableArguments = List<String>.from(platform.executableArguments),
+        packageRoot = platform
+            .packageRoot, // ignore: deprecated_member_use_from_same_package
         packageConfig = platform.packageConfig,
-        version = platform.version,
-        stdinSupportsAnsi = platform.stdinSupportsAnsi,
-        stdoutSupportsAnsi = platform.stdoutSupportsAnsi,
-        localeName = platform.localeName;
+        _version = platform.version,
+        _stdinSupportsAnsi = platform.stdinSupportsAnsi,
+        _stdoutSupportsAnsi = platform.stdoutSupportsAnsi,
+        _localeName = platform.localeName;
 
   /// Creates a new [FakePlatform] with properties extracted from the encoded
   /// JSON string.
@@ -80,50 +95,72 @@
   }
 
   @override
-  int numberOfProcessors;
+  int get numberOfProcessors => _throwIfNull(_numberOfProcessors);
+  int? _numberOfProcessors;
 
   @override
-  String pathSeparator;
+  String get pathSeparator => _throwIfNull(_pathSeparator);
+  String? _pathSeparator;
 
   @override
-  String operatingSystem;
+  String get operatingSystem => _throwIfNull(_operatingSystem);
+  String? _operatingSystem;
 
   @override
-  String operatingSystemVersion;
+  String get operatingSystemVersion => _throwIfNull(_operatingSystemVersion);
+  String? _operatingSystemVersion;
 
   @override
-  String localHostname;
+  String get localHostname => _throwIfNull(_localHostname);
+  String? _localHostname;
 
   @override
-  Map<String, String> environment;
+  Map<String, String> get environment => _throwIfNull(_environment);
+  Map<String, String>? _environment;
 
   @override
-  String executable;
+  String get executable => _throwIfNull(_executable);
+  String? _executable;
 
   @override
-  String resolvedExecutable;
+  String get resolvedExecutable => _throwIfNull(_resolvedExecutable);
+  String? _resolvedExecutable;
 
   @override
-  Uri script;
+  Uri get script => _throwIfNull(_script);
+  Uri? _script;
 
   @override
-  List<String> executableArguments;
+  List<String> get executableArguments => _throwIfNull(_executableArguments);
+  List<String>? _executableArguments;
 
   @override
-  String packageRoot;
+  String? packageRoot;
 
   @override
-  String packageConfig;
+  String? packageConfig;
 
   @override
-  String version;
+  String get version => _throwIfNull(_version);
+  String? _version;
 
   @override
-  bool stdinSupportsAnsi;
+  bool get stdinSupportsAnsi => _throwIfNull(_stdinSupportsAnsi);
+  bool? _stdinSupportsAnsi;
 
   @override
-  bool stdoutSupportsAnsi;
+  bool get stdoutSupportsAnsi => _throwIfNull(_stdoutSupportsAnsi);
+  bool? _stdoutSupportsAnsi;
 
   @override
-  String localeName;
+  String get localeName => _throwIfNull(_localeName);
+  String? _localeName;
+
+  T _throwIfNull<T>(T? value) {
+    if (value == null) {
+      throw StateError(
+          'Tried to read property of FakePlatform but it was unset.');
+    }
+    return value;
+  }
 }
diff --git a/pubspec.yaml b/pubspec.yaml
index 1e3c40a..6f22a33 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,5 @@
 name: platform
-version: 2.2.1
-authors:
-- Todd Volkert <tvolkert@google.com>
+version: 3.0.0-nullsafety
 description: A pluggable, mockable platform abstraction for Dart.
 homepage: https://github.com/google/platform.dart
 
@@ -9,4 +7,4 @@
   test: ^1.0.0
 
 environment:
-  sdk: '>=1.24.0-dev.0.0 <3.0.0'
+  sdk: '>=2.9.0 <3.0.0'
diff --git a/test/fake_platform_test.dart b/test/fake_platform_test.dart
index 90d8209..0ef8d23 100644
--- a/test/fake_platform_test.dart
+++ b/test/fake_platform_test.dart
@@ -18,7 +18,9 @@
   expect(actual.resolvedExecutable, expected.resolvedExecutable);
   expect(actual.script, expected.script);
   expect(actual.executableArguments, expected.executableArguments);
-  expect(actual.packageRoot, expected.packageRoot);
+  expect(
+      actual.packageRoot, // ignore: deprecated_member_use_from_same_package
+      expected.packageRoot); // ignore: deprecated_member_use_from_same_package
   expect(actual.packageConfig, expected.packageConfig);
   expect(actual.version, expected.version);
   expect(actual.localeName, expected.localeName);
@@ -87,4 +89,21 @@
       });
     });
   });
+
+  test('Throws when unset non-null values are read', () {
+    final FakePlatform platform = FakePlatform();
+
+    expect(() => platform.numberOfProcessors, throwsA(isStateError));
+    expect(() => platform.pathSeparator, throwsA(isStateError));
+    expect(() => platform.operatingSystem, throwsA(isStateError));
+    expect(() => platform.operatingSystemVersion, throwsA(isStateError));
+    expect(() => platform.localHostname, throwsA(isStateError));
+    expect(() => platform.environment, throwsA(isStateError));
+    expect(() => platform.executable, throwsA(isStateError));
+    expect(() => platform.resolvedExecutable, throwsA(isStateError));
+    expect(() => platform.script, throwsA(isStateError));
+    expect(() => platform.executableArguments, throwsA(isStateError));
+    expect(() => platform.version, throwsA(isStateError));
+    expect(() => platform.localeName, throwsA(isStateError));
+  });
 }