Fix running test_with_coverage non-directly (#381)

* Fix running test_with_coverage non-directly

* Add tests and improve determining what command to call

* Use format and collect main functions directly

* Add missing newline

* Bump version, add changelog entry and fix license
diff --git a/.gitignore b/.gitignore
index bc117f9..1a25097 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@
 # Temp files
 *~
 coverage/
+var/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0aff1ef..65ad06c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.3.1
+* Fix running `dart pub global run coverage:test_with_coverage` or 
+  `dart run coverage:test_with_coverage`
+
 ## 1.3.0 - 2022-5-11
 
 * Bump the minimum Dart SDK version to 2.15.0
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 838aa11..829759f 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -4,6 +4,9 @@
   strong-mode:
     implicit-casts: false
 
+  exclude:
+    - var/**
+
 linter:
   rules:
     # these rules are documented on and in the same order as
diff --git a/bin/test_with_coverage.dart b/bin/test_with_coverage.dart
index 38aed5e..53e9a7f 100644
--- a/bin/test_with_coverage.dart
+++ b/bin/test_with_coverage.dart
@@ -11,6 +11,9 @@
 import 'package:package_config/package_config.dart';
 import 'package:path/path.dart' as path;
 
+import 'collect_coverage.dart' as collect_coverage;
+import 'format_coverage.dart' as format_coverage;
+
 final allProcesses = <Process>[];
 
 Future<void> dartRun(List<String> args,
@@ -145,7 +148,6 @@
 
 Future<void> main(List<String> arguments) async {
   final flags = await parseArgs(arguments);
-  final thisDir = path.dirname(Platform.script.path);
   final outJson = path.join(flags.outDir, 'coverage.json');
   final outLcov = path.join(flags.outDir, 'lcov.info');
 
@@ -175,9 +177,7 @@
   });
   final serviceUri = await serviceUriCompleter.future;
 
-  await dartRun([
-    'run',
-    'collect_coverage.dart',
+  await collect_coverage.main([
     '--wait-paused',
     '--resume-isolates',
     '--uri=$serviceUri',
@@ -186,12 +186,10 @@
     if (flags.functionCoverage) '--function-coverage',
     '-o',
     outJson,
-  ], workingDir: thisDir);
+  ]);
   await testProcess;
 
-  await dartRun([
-    'run',
-    'format_coverage.dart',
+  await format_coverage.main([
     '--lcov',
     '--check-ignore',
     '--package=${flags.packageDir}',
@@ -199,6 +197,6 @@
     outJson,
     '-o',
     outLcov,
-  ], workingDir: thisDir);
+  ]);
   exit(0);
 }
diff --git a/pubspec.yaml b/pubspec.yaml
index 75539cf..9d4dfc9 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: coverage
-version: 1.3.0
+version: 1.3.1
 description: Coverage data manipulation and formatting
 repository: https://github.com/dart-lang/coverage
 
diff --git a/test/test_with_coverage_package/main.dart b/test/test_with_coverage_package/main.dart
new file mode 100644
index 0000000..ac94d3c
--- /dev/null
+++ b/test/test_with_coverage_package/main.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+void main() {}
diff --git a/test/test_with_coverage_package/pubspec.yaml b/test/test_with_coverage_package/pubspec.yaml
new file mode 100644
index 0000000..7507f20
--- /dev/null
+++ b/test/test_with_coverage_package/pubspec.yaml
@@ -0,0 +1,9 @@
+name: coverage_integration_test_for_test_with_coverage
+publish_to: none
+
+environment:
+  sdk: '>=2.15.0 <3.0.0'
+
+dependencies:
+  coverage:
+    path: ../../
diff --git a/test/test_with_coverage_test.dart b/test/test_with_coverage_test.dart
new file mode 100644
index 0000000..241f63b
--- /dev/null
+++ b/test/test_with_coverage_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// 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 'dart:io';
+
+import 'package:path/path.dart' as p;
+import 'package:test/test.dart';
+
+// this package
+final _pkgDir = p.absolute('');
+final _testWithCoveragePath = p.join(_pkgDir, 'bin', 'test_with_coverage.dart');
+
+// test package
+final _testPkgDirPath = p.join(_pkgDir, 'test', 'test_with_coverage_package');
+final _testPkgExePath = p.join(_testPkgDirPath, 'main.dart');
+
+/// Override PUB_CACHE
+///
+/// Use a subdirectory different from `test/` just in case there is a problem
+/// with the clean up. If other packages are present under the `test/`
+/// subdirectory their tests may accidentally get run when running `dart test`
+final _pubCachePathInTestPkgSubDir = p.join(_pkgDir, 'var', 'pub-cache');
+final _env = {'PUB_CACHE': _pubCachePathInTestPkgSubDir};
+
+int _port = 9300;
+
+void main() {
+  setUpAll(() {
+    final localPub = _runSync(['pub', 'get']);
+    assert(_wasSuccessful(localPub));
+
+    final globalPub =
+        _runSync(['pub', 'global', 'activate', '-s', 'git', _pkgDir]);
+    assert(_wasSuccessful(globalPub));
+  });
+
+  tearDownAll(() {
+    for (final entry in [
+      Directory(_pubCachePathInTestPkgSubDir),
+      Directory(p.join(_testPkgDirPath, '.dart_tool')),
+      Directory(p.join(_testPkgDirPath, 'coverage')),
+      File(p.join(_testPkgDirPath, '.packages')),
+      File(p.join(_testPkgDirPath, 'pubspec.lock')),
+    ]) {
+      if (entry.existsSync()) {
+        entry.deleteSync(recursive: true);
+      }
+    }
+  });
+
+  test('dart run bin/test_with_coverage.dart', () {
+    return _runTest(['run', _testWithCoveragePath]).then(_expectSuccessful);
+  });
+  test('dart run coverage:test_with_coverage', () {
+    return _runTest(['run', 'coverage:test_with_coverage'])
+        .then(_expectSuccessful);
+  });
+  test('dart pub global run coverage:test_with_coverage', () {
+    return _runTest(['pub', 'global', 'run', 'coverage:test_with_coverage'])
+        .then(_expectSuccessful);
+  });
+}
+
+ProcessResult _runSync(List<String> args) =>
+    Process.runSync(Platform.executable, args,
+        workingDirectory: _testPkgDirPath, environment: _env);
+
+Future<ProcessResult> _run(List<String> args) =>
+    Process.run(Platform.executable, args,
+        workingDirectory: _testPkgDirPath, environment: _env);
+
+bool _wasSuccessful(ProcessResult result) => result.exitCode == 0;
+
+void _expectSuccessful(ProcessResult result) {
+  if (!_wasSuccessful(result)) {
+    fail(
+      "Process excited with exit code: ${result.exitCode}. Stderr: ${result.stderr}",
+    );
+  }
+}
+
+Future<ProcessResult> _runTest(List<String> invokeArgs) =>
+    _run([...invokeArgs, '--port', '${_port++}', '--test', _testPkgExePath]);