Skip to content

Commit a283d70

Browse files
authored
Make health testable (#224)
* Add `ignore` flag to health workflows * Switch to glob * Fix multiline * Add default glob * Delete multiline * Fix errors * Fix health commenting * Propagate ignore * Switch to specific ignores * Add defaults * Add test repos * Start adding health tests * Add golden files * Fix changelog * Fix coverage * Fix breaking * More fixes * Revert "Add defaults" This reverts commit 2bacc71. * Remove ignores * Remove ignores from yamls * Remove from firehose * Remove from github * Remove from repo * Add changelog * Move goldens * Fix glob issue * Add test data to pubignore * Switch SDK version * Move stuff around * Fix imports * Give test a name * Sort license files * Switch debug messages * Add debug string * Add debug * Add activate global * Remove debugging * Fix coverage upload * fix erros
1 parent f61a550 commit a283d70

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+839
-126
lines changed

.github/workflows/health.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ on:
7878
type: boolean
7979
required: false
8080
use-flutter:
81-
description: >-
82-
Whether to setup Flutter in this workflow.
81+
description: Whether to setup Flutter in this workflow.
8382
default: false
8483
required: false
8584
type: boolean

.github/workflows/health_base.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ on:
4545
type: boolean
4646
required: false
4747
use-flutter:
48-
description: >-
49-
Whether to setup Flutter in this workflow.
48+
description: Whether to setup Flutter in this workflow.
5049
default: false
5150
required: false
5251
type: boolean
@@ -131,7 +130,7 @@ jobs:
131130
if: ${{ '$action_state' == 1 }}
132131

133132
- name: Upload coverage to Coveralls
134-
if: ${{ inputs.upload_coverage }}
133+
if: ${{ inputs.upload_coverage && inputs.check == 'coverage' }}
135134
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949
136135
with:
137136
format: lcov

pkgs/firehose/.pubignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test_data/

pkgs/firehose/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.6.0
2+
3+
- Make the health workflow testable with golden tests.
4+
15
## 0.5.3
26

37
- Allow experiments to be enabled for Dart.

pkgs/firehose/analysis_options.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
include: package:dart_flutter_team_lints/analysis_options.yaml
2+
3+
analyzer:
4+
exclude:
5+
- test_data/*

pkgs/firehose/bin/health.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'dart:io';
66

77
import 'package:args/args.dart';
8+
import 'package:firehose/src/github.dart';
89
import 'package:firehose/src/health/health.dart';
910

1011
void main(List<String> arguments) async {
@@ -51,5 +52,6 @@ void main(List<String> arguments) async {
5152
failOn,
5253
coverageWeb,
5354
experiments,
55+
GithubApi(),
5456
).healthCheck();
5557
}

pkgs/firehose/lib/firehose.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Saving existing comment id $existingCommentId to file ${idFile.path}''');
9191
}
9292

9393
Future<VerificationResults> verify(GithubApi github) async {
94-
var repo = Repository();
94+
var repo = Repository(directory);
9595
var packages = repo.locatePackages();
9696

9797
var pub = Pub();

pkgs/firehose/lib/src/github.dart

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,13 @@ class GithubApi {
1919

2020
static Map<String, String> get _env => Platform.environment;
2121

22-
/// When true, details of any RPC error are printed to the console.
23-
final bool verbose;
24-
25-
GithubApi({this.verbose = false, RepositorySlug? repoSlug, int? issueNumber})
22+
GithubApi({RepositorySlug? repoSlug, int? issueNumber})
2623
: _repoSlug = repoSlug,
2724
_issueNumber = issueNumber;
2825

2926
final http.Client _client = DelayedClient(const Duration(milliseconds: 50));
3027

31-
late GitHub github = githubAuthToken != null
28+
late final GitHub _github = githubAuthToken != null
3229
? GitHub(
3330
auth: Authentication.withToken(githubAuthToken),
3431
client: _client,
@@ -95,7 +92,7 @@ class GithubApi {
9592
required String user,
9693
String? searchTerm,
9794
}) async {
98-
final matchingComment = await github.issues
95+
final matchingComment = await _github.issues
9996
.listCommentsByIssue(repoSlug!, issueNumber!)
10097
.map<IssueComment?>((comment) => comment)
10198
.firstWhere(
@@ -110,38 +107,41 @@ class GithubApi {
110107
return matchingComment?.id;
111108
}
112109

113-
Future<List<GitFile>> listFilesForPR() async => await github.pullRequests
114-
.listFiles(repoSlug!, issueNumber!)
115-
.map((prFile) =>
116-
GitFile(prFile.filename!, FileStatus.fromString(prFile.status!)))
117-
.toList();
110+
Future<List<GitFile>> listFilesForPR(Directory directory) async =>
111+
await _github.pullRequests
112+
.listFiles(repoSlug!, issueNumber!)
113+
.map((prFile) => GitFile(
114+
prFile.filename!,
115+
FileStatus.fromString(prFile.status!),
116+
directory,
117+
))
118+
.toList();
118119

119120
/// Write a notice message to the github log.
120121
void notice({required String message}) {
121122
print('::notice ::$message');
122123
}
123124

124125
Future<String> pullrequestBody() async {
125-
final pullRequest = await github.pullRequests.get(repoSlug!, issueNumber!);
126+
final pullRequest = await _github.pullRequests.get(repoSlug!, issueNumber!);
126127
return pullRequest.body ?? '';
127128
}
128129

129-
void close() => github.dispose();
130+
void close() => _github.dispose();
130131
}
131132

132133
class GitFile {
133134
final String filename;
134135
final FileStatus status;
136+
final Directory directory;
135137

136138
bool isInPackage(Package package) {
137-
print('Check if $relativePath is in ${package.directory.path}');
138-
return path.isWithin(package.directory.path, relativePath);
139+
return path.isWithin(package.directory.path, pathInRepository);
139140
}
140141

141-
GitFile(this.filename, this.status);
142+
String get pathInRepository => path.join(directory.path, filename);
142143

143-
String get relativePath =>
144-
path.relative(filename, from: Directory.current.path);
144+
GitFile(this.filename, this.status, this.directory);
145145

146146
@override
147147
String toString() => '$filename: $status';

pkgs/firehose/lib/src/health/changelog.dart

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,27 @@ import 'package:path/path.dart' as path;
88

99
import '../github.dart';
1010
import '../repo.dart';
11-
import '../utils.dart';
1211

1312
Future<Map<Package, List<GitFile>>> packagesWithoutChangelog(
14-
GithubApi github) async {
15-
final repo = Repository();
13+
GithubApi github,
14+
Directory directory,
15+
) async {
16+
final repo = Repository(directory);
1617
final packages = repo.locatePackages();
1718

18-
final files = await github.listFilesForPR();
19+
final files = await github.listFilesForPR(directory);
1920

20-
var packagesWithoutChangedChangelog =
21-
collectPackagesWithoutChangelogChanges(packages, files);
21+
var packagesWithoutChangedChangelog = collectPackagesWithoutChangelogChanges(
22+
packages,
23+
files,
24+
directory,
25+
);
2226

2327
print('Collecting files without license headers in those packages:');
2428
var packagesWithChanges = <Package, List<GitFile>>{};
2529
for (final file in files) {
2630
for (final package in packagesWithoutChangedChangelog) {
27-
if (fileNeedsEntryInChangelog(package, file.relativePath)) {
31+
if (fileNeedsEntryInChangelog(package, file.filename, directory)) {
2832
print(file);
2933
packagesWithChanges.update(
3034
package,
@@ -40,21 +44,24 @@ Done, found ${packagesWithChanges.length} packages with a need for a changelog.'
4044
}
4145

4246
List<Package> collectPackagesWithoutChangelogChanges(
43-
List<Package> packages, List<GitFile> files) {
47+
List<Package> packages,
48+
List<GitFile> files,
49+
Directory directory,
50+
) {
4451
print('Collecting packages without changed changelogs:');
45-
final packagesWithoutChangedChangelog = packages
46-
.where((package) => package.changelog.exists)
47-
.where((package) => !files
48-
.map((e) => e.relativePath)
49-
.contains(package.changelog.file.relativePath))
50-
.toList();
52+
final packagesWithoutChangedChangelog =
53+
packages.where((package) => package.changelog.exists).where((package) {
54+
return !files
55+
.map((e) => e.pathInRepository)
56+
.contains(package.changelog.file.path);
57+
}).toList();
5158
print('Done, found ${packagesWithoutChangedChangelog.length} packages.');
5259
return packagesWithoutChangedChangelog;
5360
}
5461

55-
bool fileNeedsEntryInChangelog(Package package, String file) {
62+
bool fileNeedsEntryInChangelog(Package package, String file, Directory d) {
5663
final directoryPath = package.directory.path;
57-
final directory = path.relative(directoryPath, from: Directory.current.path);
64+
final directory = path.relative(directoryPath, from: d.path);
5865
final isInPackage = path.isWithin(directory, file);
5966
final isInLib = path.isWithin(path.join(directory, 'lib'), file);
6067
final isInBin = path.isWithin(path.join(directory, 'bin'), file);

pkgs/firehose/lib/src/health/coverage.dart

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import 'dart:io';
77

8+
import 'package:collection/collection.dart';
89
import 'package:path/path.dart' as path;
910

1011
import '../github.dart';
@@ -14,39 +15,39 @@ import 'lcov.dart';
1415

1516
class Coverage {
1617
final bool coverageWeb;
18+
final Directory directory;
1719
final List<String> experiments;
1820

19-
Coverage(this.coverageWeb, this.experiments);
21+
Coverage(this.coverageWeb, this.directory, this.experiments);
2022

21-
Future<CoverageResult> compareCoverages(GithubApi github) async {
22-
var files = await github.listFilesForPR();
23-
var basePath = '../base_repo/';
23+
Future<CoverageResult> compareCoverages(
24+
GithubApi github, Directory base) async {
25+
var files = await github.listFilesForPR(directory);
2426

25-
return compareCoveragesFor(files, basePath);
27+
return compareCoveragesFor(files, base);
2628
}
2729

28-
CoverageResult compareCoveragesFor(List<GitFile> files, String basePath) {
29-
var repository = Repository();
30+
CoverageResult compareCoveragesFor(List<GitFile> files, Directory base) {
31+
var repository = Repository(directory);
3032
var packages = repository.locatePackages();
31-
print('Found packages $packages at ${Directory.current}');
33+
print('Found packages $packages at $directory');
3234

3335
var filesOfInterest = files
3436
.where((file) => path.extension(file.filename) == '.dart')
3537
.where((file) => file.status != FileStatus.removed)
36-
.where((file) => isInSomePackage(packages, file.relativePath))
37-
.where((file) => isNotATest(packages, file.relativePath))
38+
.where((file) => isInSomePackage(packages, file.filename))
39+
.where((file) => isNotATest(packages, file.filename))
3840
.toList();
3941
print('The files of interest are $filesOfInterest');
4042

41-
var base = Directory(basePath);
42-
4343
var baseRepository = Repository(base);
4444
var basePackages = baseRepository.locatePackages();
4545
print('Found packages $basePackages at $base');
4646

4747
var changedPackages = packages
4848
.where((package) =>
4949
filesOfInterest.any((file) => file.isInPackage(package)))
50+
.sortedBy((package) => package.name)
5051
.toList();
5152

5253
print('The packages of interest are $changedPackages');
@@ -59,14 +60,15 @@ class Coverage {
5960
.where((element) => element.name == package.name)
6061
.firstOrNull;
6162
final oldCoverages = getCoverage(basePackage);
62-
var filePaths = filesOfInterest
63+
var filenames = filesOfInterest
6364
.where((file) => file.isInPackage(package))
64-
.map((file) => file.relativePath);
65-
for (var filePath in filePaths) {
66-
var oldCoverage = oldCoverages[filePath];
67-
var newCoverage = newCoverages[filePath];
68-
print('Compage coverage for $filePath: $oldCoverage vs $newCoverage');
69-
coverageResult[filePath] = Change(
65+
.map((file) => file.filename)
66+
.sortedBy((filename) => filename);
67+
for (var filename in filenames) {
68+
var oldCoverage = oldCoverages[filename];
69+
var newCoverage = newCoverages[filename];
70+
print('Compage coverage for $filename: $oldCoverage vs $newCoverage');
71+
coverageResult[filename] = Change(
7072
oldCoverage: oldCoverage,
7173
newCoverage: newCoverage,
7274
);
@@ -76,14 +78,16 @@ class Coverage {
7678
}
7779

7880
bool isNotATest(List<Package> packages, String file) {
79-
return packages.every((package) =>
80-
!path.isWithin(path.join(package.directory.path, 'test'), file));
81+
return packages.every((package) => !path.isWithin(
82+
path.join(package.directory.path, 'test'),
83+
path.join(directory.path, file)));
8184
}
8285

83-
bool isInSomePackage(List<Package> packages, String file) {
84-
return packages
85-
.any((package) => path.isWithin(package.directory.path, file));
86-
}
86+
bool isInSomePackage(List<Package> packages, String file) =>
87+
packages.any((package) => path.isWithin(
88+
package.directory.path,
89+
path.join(directory.path, file),
90+
));
8791

8892
Map<String, double> getCoverage(Package? package) {
8993
if (package != null) {
@@ -103,7 +107,7 @@ Get coverage for ${package.name} by running coverage in ${package.directory.path
103107
workingDirectory: package.directory.path,
104108
);
105109
if (coverageWeb) {
106-
print('Get test coverage for web');
110+
print('Run tests with coverage for web');
107111
var resultChrome = Process.runSync(
108112
'dart',
109113
[
@@ -119,7 +123,7 @@ Get coverage for ${package.name} by running coverage in ${package.directory.path
119123
print(resultChrome.stdout);
120124
print(resultChrome.stderr);
121125
}
122-
print('Get test coverage for vm');
126+
print('Run tests with coverage for vm');
123127
var resultVm = Process.runSync(
124128
'dart',
125129
[
@@ -130,8 +134,9 @@ Get coverage for ${package.name} by running coverage in ${package.directory.path
130134
],
131135
workingDirectory: package.directory.path,
132136
);
133-
print(resultVm.stdout);
134-
print(resultVm.stderr);
137+
print('dart test stdout: ${resultVm.stdout}');
138+
print('dart test stderr: ${resultVm.stderr}');
139+
print('Compute coverage from runs');
135140
var resultLcov = Process.runSync(
136141
'dart',
137142
[
@@ -149,8 +154,8 @@ Get coverage for ${package.name} by running coverage in ${package.directory.path
149154
],
150155
workingDirectory: package.directory.path,
151156
);
152-
print(resultLcov.stdout);
153-
print(resultLcov.stderr);
157+
print('dart coverage stdout: ${resultLcov.stdout}');
158+
print('dart coverage stderr: ${resultLcov.stderr}');
154159
return parseLCOV(
155160
path.join(package.directory.path, 'coverage/lcov.info'),
156161
relativeTo: package.repository.baseDirectory.path,

0 commit comments

Comments
 (0)