Add more details to ArgumentError if process resolution fails (#59)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d5561b7..ec52840 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,9 @@
-#### 4.0.1
+#### 4.1.0
 
 * Fix the signatures of `ProcessManager.run`, `.runSync`, and `.start` to be
   consistent with `LocalProcessManager`'s.
+* Added more details to the `ArgumentError` thrown when a command cannot be resolved
+  to an executable.
 
 #### 4.0.0
 
diff --git a/lib/src/interface/common.dart b/lib/src/interface/common.dart
index a3aed86..2f6a5c9 100644
--- a/lib/src/interface/common.dart
+++ b/lib/src/interface/common.dart
@@ -53,9 +53,9 @@
   String? workingDirectory, {
   Platform platform = const LocalPlatform(),
   FileSystem fs = const LocalFileSystem(),
+  bool errorOnNull = false,
 }) {
   assert(_osToPathStyle[platform.operatingSystem] == fs.path.style.name);
-
   try {
     workingDirectory ??= fs.currentDirectory.path;
   } on FileSystemException {
@@ -89,6 +89,11 @@
       return path;
     }
   }
+  if (errorOnNull) {
+    throw ArgumentError('Failed to resolve $command to an executable.\n'
+        'workingDirectory: $workingDirectory, '
+        'candidates: ${candidates.length}');
+  }
   return null;
 }
 
diff --git a/lib/src/interface/local_process_manager.dart b/lib/src/interface/local_process_manager.dart
index a16b753..fe5041c 100644
--- a/lib/src/interface/local_process_manager.dart
+++ b/lib/src/interface/local_process_manager.dart
@@ -105,7 +105,8 @@
 
   @override
   bool canRun(covariant String executable, {String? workingDirectory}) =>
-      getExecutablePath(executable, workingDirectory) != null;
+      getExecutablePath(executable, workingDirectory, errorOnNull: false) !=
+      null;
 
   @override
   bool killPid(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) {
@@ -119,7 +120,8 @@
   if (runInShell) {
     return commandName;
   }
-  String? exe = getExecutablePath(commandName, workingDirectory);
+  String? exe =
+      getExecutablePath(commandName, workingDirectory, errorOnNull: true);
   if (exe == null) {
     throw ArgumentError('Cannot find executable for $commandName.');
   }
diff --git a/pubspec.yaml b/pubspec.yaml
index 2c6229d..c854b97 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: process
-version: 4.0.1
+version: 4.1.0
 description: A pluggable, mockable process invocation abstraction for Dart.
 homepage: https://github.com/google/process.dart
 
diff --git a/test/src/interface/common_test.dart b/test/src/interface/common_test.dart
index 6a3e2df..cbfd180 100644
--- a/test/src/interface/common_test.dart
+++ b/test/src/interface/common_test.dart
@@ -192,6 +192,29 @@
         expect(executablePath, isNull);
       });
 
+      test('not found with errorOnNull throws exception with match state', () {
+        String command = 'foo.exe';
+        dynamic error;
+        try {
+          getExecutablePath(
+            command,
+            workingDir.path,
+            platform: platform,
+            fs: fs,
+            errorOnNull: true,
+          );
+          fail('Expected to throw');
+        } catch (err) {
+          error = err;
+        }
+
+        expect(error, isA<ArgumentError>());
+        expect(
+            error.toString(),
+            contains(
+                'workingDirectory: C:\\.tmp_rand0\\work_dir_rand0, candidates: 2'));
+      });
+
       test('when path has spaces', () {
         expect(
             sanitizeExecutablePath('Program Files\\bla.exe',
@@ -298,6 +321,29 @@
         expect(executablePath, isNull);
       });
 
+      test('not found with errorOnNull throws exception with match state', () {
+        String command = 'foo';
+        dynamic error;
+        try {
+          getExecutablePath(
+            command,
+            workingDir.path,
+            platform: platform,
+            fs: fs,
+            errorOnNull: true,
+          );
+          fail('Expected to throw');
+        } catch (err) {
+          error = err;
+        }
+
+        expect(error, isA<ArgumentError>());
+        expect(
+            error.toString(),
+            contains(
+                'workingDirectory: /.tmp_rand0/work_dir_rand0, candidates: 2'));
+      });
+
       test('when path has spaces', () {
         expect(
             sanitizeExecutablePath('/usr/local/bin/foo bar',