Pubspec.environment: never null, better validation for bad values
diff --git a/pkgs/pubspec_parse/lib/src/pubspec.dart b/pkgs/pubspec_parse/lib/src/pubspec.dart
index b578596..169e719 100644
--- a/pkgs/pubspec_parse/lib/src/pubspec.dart
+++ b/pkgs/pubspec_parse/lib/src/pubspec.dart
@@ -58,7 +58,7 @@
     this.version,
     String author,
     List<String> authors,
-    this.environment,
+    Map<String, VersionConstraint> environment,
     this.homepage,
     this.documentation,
     this.description,
@@ -66,6 +66,7 @@
     Map<String, Dependency> devDependencies,
     Map<String, Dependency> dependencyOverrides,
   })  : this.authors = _normalizeAuthors(author, authors),
+        this.environment = environment ?? const {},
         this.dependencies = dependencies ?? const {},
         this.devDependencies = devDependencies ?? const {},
         this.dependencyOverrides = dependencyOverrides ?? const {} {
@@ -114,7 +115,8 @@
 Version _versionFromString(String input) => new Version.parse(input);
 
 Map<String, VersionConstraint> _environmentMap(Map source) =>
-    source.map((key, value) {
+    source.map((k, value) {
+      var key = k as String;
       if (key == 'dart') {
         // github.com/dart-lang/pub/blob/d84173eeb03c3/lib/src/pubspec.dart#L342
         // 'dart' is not allowed as a key!
@@ -122,13 +124,17 @@
             source, 'dart', 'Use "sdk" to for Dart SDK constraints.');
       }
 
-      VersionConstraint constraint;
-      try {
-        constraint = new VersionConstraint.parse(value as String);
-      } on FormatException catch (e) {
-        throw new CheckedFromJsonException(
-            source, key as String, 'Pubspec', e.message);
+      if (value is String) {
+        VersionConstraint constraint;
+        try {
+          constraint = new VersionConstraint.parse(value);
+        } on FormatException catch (e) {
+          throw new CheckedFromJsonException(source, key, 'Pubspec', e.message);
+        }
+
+        return new MapEntry(key, constraint);
       }
 
-      return new MapEntry(key as String, constraint);
+      throw new CheckedFromJsonException(
+          source, key, 'VersionConstraint', '`$value` is not a String.');
     });
diff --git a/pkgs/pubspec_parse/test/parse_test.dart b/pkgs/pubspec_parse/test/parse_test.dart
index d163e5a..1d7fcd8 100644
--- a/pkgs/pubspec_parse/test/parse_test.dart
+++ b/pkgs/pubspec_parse/test/parse_test.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.
 
+import 'package:pub_semver/pub_semver.dart';
 import 'package:test/test.dart';
 
 import 'test_utils.dart';
@@ -16,6 +17,7 @@
     // ignore: deprecated_member_use
     expect(value.author, isNull);
     expect(value.authors, isEmpty);
+    expect(value.environment, isEmpty);
     expect(value.documentation, isNull);
     expect(value.dependencies, isEmpty);
     expect(value.devDependencies, isEmpty);
@@ -23,21 +25,25 @@
   });
 
   test('all fields set', () {
+    var constraint = new Version.parse('1.2.3');
     var value = parse({
       'name': 'sample',
-      'version': '1.2.3',
+      'version': constraint.toString(),
       'author': 'name@example.com',
+      'environment': {'sdk': '1.2.3'},
       'description': 'description',
       'homepage': 'homepage',
       'documentation': 'documentation'
     });
     expect(value.name, 'sample');
-    expect(value.version.toString(), '1.2.3');
+    expect(value.version, constraint);
     expect(value.description, 'description');
     expect(value.homepage, 'homepage');
     // ignore: deprecated_member_use
     expect(value.author, 'name@example.com');
     expect(value.authors, ['name@example.com']);
+    expect(value.environment, hasLength(1));
+    expect(value.environment, containsPair('sdk', constraint));
     expect(value.documentation, 'documentation');
     expect(value.dependencies, isEmpty);
     expect(value.devDependencies, isEmpty);
@@ -126,6 +132,26 @@
   ^^^^^^''');
     });
 
+    test('environment values cannot be null', () {
+      expectParseThrows({
+        'name': 'sample',
+        'environment': {'sdk': null}
+      }, r'''
+line 4, column 10: `null` is not a String.
+  "sdk": null
+         ^^^^^''');
+    });
+
+    test('environment values cannot be int', () {
+      expectParseThrows({
+        'name': 'sample',
+        'environment': {'sdk': 42}
+      }, r'''
+line 4, column 10: `42` is not a String.
+  "sdk": 42
+         ^^^''');
+    });
+
     test('invalid version', () {
       expectParseThrows({'name': 'sample', 'version': 'invalid'}, r'''
 line 3, column 13: Unsupported value for `version`.