Warn about deprecated LICENSE and README.md file names (#2381)

diff --git a/lib/src/validator/license.dart b/lib/src/validator/license.dart
index 4f012fc..1646b11 100644
--- a/lib/src/validator/license.dart
+++ b/lib/src/validator/license.dart
@@ -16,17 +16,22 @@
   @override
   Future validate() {
     return Future.sync(() {
-      var licenseLike =
+      final licenseLike =
           RegExp(r'^(([a-zA-Z0-9]+[-_])?(LICENSE|COPYING)|UNLICENSE)(\..*)?$');
-      if (entrypoint.root
+      final candidates = entrypoint.root
           .listFiles(recursive: false, useGitIgnore: true)
           .map(path.basename)
-          .any(licenseLike.hasMatch)) {
+          .where(licenseLike.hasMatch);
+      if (candidates.isNotEmpty) {
+        if (!candidates.contains('LICENSE')) {
+          final firstCandidate = candidates.first;
+          warnings.add('Please consider renaming $firstCandidate to `LICENSE`. '
+              'See https://dart.dev/tools/pub/publishing#important-files.');
+        }
         return;
       }
 
-      errors.add(
-          'You must have a COPYING, LICENSE or UNLICENSE file in the root directory.\n'
+      errors.add('You must have a LICENSE file in the root directory.\n'
           'An open-source license helps ensure people can legally use your '
           'code.');
     });
diff --git a/lib/src/validator/readme.dart b/lib/src/validator/readme.dart
index 716df0d..85de479 100644
--- a/lib/src/validator/readme.dart
+++ b/lib/src/validator/readme.dart
@@ -5,6 +5,8 @@
 import 'dart:async';
 import 'dart:convert';
 
+import 'package:path/path.dart' as path;
+
 import '../entrypoint.dart';
 import '../io.dart';
 import '../validator.dart';
@@ -23,6 +25,11 @@
         return;
       }
 
+      if (path.basename(readme) != 'README.md') {
+        warnings.add('Please consider renaming $readme to `README.md`. '
+            'See https://dart.dev/tools/pub/publishing#important-files.');
+      }
+
       var bytes = readBinaryFile(readme);
       try {
         // utf8.decode doesn't allow invalid UTF-8.
diff --git a/test/validator/license_test.dart b/test/validator/license_test.dart
index 33726cb..eb77ef4 100644
--- a/test/validator/license_test.dart
+++ b/test/validator/license_test.dart
@@ -18,32 +18,45 @@
 
 void main() {
   group('should consider a package valid if it', () {
-    setUp(d.validPackage.create);
-
-    test('looks normal', () => expectNoValidationError(license));
-
-    test('has a COPYING file', () async {
-      deleteEntry(path.join(d.sandbox, appPath, 'LICENSE'));
-      await d.file(path.join(appPath, 'COPYING'), '').create();
+    test('looks normal', () async {
+      await d.validPackage.create();
       expectNoValidationError(license);
     });
 
-    test('has an UNLICENSE file', () async {
-      deleteEntry(path.join(d.sandbox, appPath, 'LICENSE'));
+    test('has both LICENSE and UNLICENSE file', () async {
+      await d.validPackage.create();
       await d.file(path.join(appPath, 'UNLICENSE'), '').create();
       expectNoValidationError(license);
     });
+  });
 
-    test('has a prefixed LICENSE file', () async {
+  group('should warn if it', () {
+    test('has only a COPYING file', () async {
+      await d.validPackage.create();
       deleteEntry(path.join(d.sandbox, appPath, 'LICENSE'));
-      await d.file(path.join(appPath, 'MIT_LICENSE'), '').create();
-      expectNoValidationError(license);
+      await d.file(path.join(appPath, 'COPYING'), '').create();
+      expectValidationWarning(license);
     });
 
-    test('has a suffixed LICENSE file', () async {
+    test('has only an UNLICENSE file', () async {
+      await d.validPackage.create();
+      deleteEntry(path.join(d.sandbox, appPath, 'LICENSE'));
+      await d.file(path.join(appPath, 'UNLICENSE'), '').create();
+      expectValidationWarning(license);
+    });
+
+    test('has only a prefixed LICENSE file', () async {
+      await d.validPackage.create();
+      deleteEntry(path.join(d.sandbox, appPath, 'LICENSE'));
+      await d.file(path.join(appPath, 'MIT_LICENSE'), '').create();
+      expectValidationWarning(license);
+    });
+
+    test('has only a suffixed LICENSE file', () async {
+      await d.validPackage.create();
       deleteEntry(path.join(d.sandbox, appPath, 'LICENSE'));
       await d.file(path.join(appPath, 'LICENSE.md'), '').create();
-      expectNoValidationError(license);
+      expectValidationWarning(license);
     });
   });
 
diff --git a/test/validator/readme_test.dart b/test/validator/readme_test.dart
index 7eeb834..7a45278 100644
--- a/test/validator/readme_test.dart
+++ b/test/validator/readme_test.dart
@@ -20,16 +20,13 @@
   setUp(d.validPackage.create);
 
   group('should consider a package valid if it', () {
-    test('looks normal', () => expectNoValidationError(readme));
-
-    test('has a non-primary readme', () async {
-      deleteEntry(p.join(d.sandbox, 'myapp/README.md'));
-
-      await d.dir(appPath, [d.file('README.whatever')]).create();
+    test('looks normal', () async {
+      await d.validPackage.create();
       expectNoValidationError(readme);
     });
 
     test('has a non-primary readme with invalid utf-8', () async {
+      await d.validPackage.create();
       await d.dir(appPath, [
         d.file('README.x.y.z', [192])
       ]).create();
@@ -37,6 +34,7 @@
     });
 
     test('has a gitignored README with invalid utf-8', () async {
+      await d.validPackage.create();
       var repo = d.git(appPath, [
         d.file('README', [192]),
         d.file('.gitignore', 'README')
@@ -47,21 +45,46 @@
   });
 
   group('should consider a package invalid if it', () {
-    test('has no README', () {
+    test('has no README', () async {
+      await d.validPackage.create();
+
       deleteEntry(p.join(d.sandbox, 'myapp/README.md'));
       expectValidationWarning(readme);
     });
 
     test('has only a .gitignored README', () async {
+      await d.validPackage.create();
       await d.git(appPath, [d.file('.gitignore', 'README.md')]).create();
       expectValidationWarning(readme);
     });
 
     test('has a primary README with invalid utf-8', () async {
+      await d.validPackage.create();
       await d.dir(appPath, [
         d.file('README', [192])
       ]).create();
       expectValidationWarning(readme);
     });
+
+    test('has only a non-primary readme', () async {
+      await d.validPackage.create();
+      deleteEntry(p.join(d.sandbox, 'myapp/README.md'));
+      await d.dir(appPath, [d.file('README.whatever')]).create();
+      expectValidationWarning(readme);
+    });
+
+    test('Uses only deprecated readme name .markdown', () async {
+      await d.validPackage.create();
+      deleteEntry(p.join(d.sandbox, 'myapp/README.md'));
+      await d.dir(appPath, [d.file('README.markdown')]).create();
+      expectValidationWarning(readme);
+    });
+
+    test('Uses only deprecated readme name .mdown', () async {
+      await d.validPackage.create();
+      deleteEntry(p.join(d.sandbox, 'myapp/README.md'));
+      await d.dir(appPath, [d.file('README.mdown')]).create();
+      expectValidationWarning(readme);
+    });
   });
 }