Skip to content

Commit 5e9e1df

Browse files
authored
Add automatic SDK warning comparison between versions to dartdoc (#1572)
* New class memoizer for simplifying Dartdoc's caching * Reduce copy-pasting to bare minimum * One more tweak * dartfmt * Add the motivating case * systemTemp was not a constant * Cleanup * Review comments * dartfmt * First version of SDK warnings comparison * dartfmt * Eliminate the if statement altogether. * dartfmt * Reorganize code a bit * dartfmt * review comments
1 parent 29a676a commit 5e9e1df

File tree

3 files changed

+294
-100
lines changed

3 files changed

+294
-100
lines changed

lib/src/io_utils.dart

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
/// This is a helper library to make working with io easier.
66
library dartdoc.io_utils;
77

8+
import 'dart:async';
9+
import 'dart:convert';
810
import 'dart:io';
911

1012
import 'package:path/path.dart' as path;
@@ -60,3 +62,158 @@ String getFileNameFor(String name) =>
6062
final libraryNameRegexp = new RegExp('[.:]');
6163
final partOfRegexp = new RegExp('part of ');
6264
final newLinePartOfRegexp = new RegExp('\npart of ');
65+
66+
final RegExp quotables = new RegExp(r'[ "\r\n\$]');
67+
68+
class SubprocessLauncher {
69+
final String context;
70+
Map<String, String> _environment;
71+
72+
Map<String, String> get environment => _environment;
73+
74+
String get prefix => context.isNotEmpty ? '$context: ' : '';
75+
76+
// from flutter:dev/tools/dartdoc.dart, modified
77+
static void _printStream(Stream<List<int>> stream, Stdout output,
78+
{String prefix: '', Iterable<String> Function(String line) filter}) {
79+
assert(prefix != null);
80+
if (filter == null) filter = (line) => [line];
81+
stream
82+
.transform(UTF8.decoder)
83+
.transform(const LineSplitter())
84+
.expand(filter)
85+
.listen((String line) {
86+
if (line != null) {
87+
output.write('$prefix$line'.trim());
88+
output.write('\n');
89+
}
90+
});
91+
}
92+
93+
SubprocessLauncher(this.context, [Map<String, String> environment]) {
94+
if (environment == null) this._environment = new Map();
95+
}
96+
97+
/// A wrapper around start/await process.exitCode that will display the
98+
/// output of the executable continuously and fail on non-zero exit codes.
99+
/// It will also parse any valid JSON objects (one per line) it encounters
100+
/// on stdout/stderr, and return them. Returns null if no JSON objects
101+
/// were encountered.
102+
///
103+
/// Makes running programs in grinder similar to set -ex for bash, even on
104+
/// Windows (though some of the bashisms will no longer make sense).
105+
/// TODO(jcollins-g): move this to grinder?
106+
Future<Iterable<Map>> runStreamed(String executable, List<String> arguments,
107+
{String workingDirectory}) async {
108+
List<Map> jsonObjects;
109+
110+
/// Allow us to pretend we didn't pass the JSON flag in to dartdoc by
111+
/// printing what dartdoc would have printed without it, yet storing
112+
/// json objects into [jsonObjects].
113+
Iterable<String> jsonCallback(String line) {
114+
Map result;
115+
try {
116+
result = json.decoder.convert(line);
117+
} catch (FormatException) {}
118+
if (result != null) {
119+
if (jsonObjects == null) {
120+
jsonObjects = new List();
121+
}
122+
jsonObjects.add(result);
123+
if (result.containsKey('message')) {
124+
line = result['message'];
125+
} else if (result.containsKey('data')) {
126+
line = result['data']['text'];
127+
}
128+
}
129+
return line.split('\n');
130+
}
131+
132+
stderr.write('$prefix+ ');
133+
if (workingDirectory != null) stderr.write('(cd "$workingDirectory" && ');
134+
if (environment != null) {
135+
stderr.write(environment.keys.map((String key) {
136+
if (environment[key].contains(quotables)) {
137+
return "$key='${environment[key]}'";
138+
} else {
139+
return "$key=${environment[key]}";
140+
}
141+
}).join(' '));
142+
stderr.write(' ');
143+
}
144+
stderr.write('$executable');
145+
if (arguments.isNotEmpty) {
146+
for (String arg in arguments) {
147+
if (arg.contains(quotables)) {
148+
stderr.write(" '$arg'");
149+
} else {
150+
stderr.write(" $arg");
151+
}
152+
}
153+
}
154+
if (workingDirectory != null) stderr.write(')');
155+
stderr.write('\n');
156+
Process process = await Process.start(executable, arguments,
157+
workingDirectory: workingDirectory, environment: environment);
158+
159+
_printStream(process.stdout, stdout, prefix: prefix, filter: jsonCallback);
160+
_printStream(process.stderr, stderr, prefix: prefix, filter: jsonCallback);
161+
await process.exitCode;
162+
163+
int exitCode = await process.exitCode;
164+
if (exitCode != 0) {
165+
throw new ProcessException(executable, arguments,
166+
"SubprocessLauncher got non-zero exitCode", exitCode);
167+
}
168+
return jsonObjects;
169+
}
170+
}
171+
172+
/// Output formatter for comparing warnings.
173+
String printWarningDelta(String title, String dartdocOriginalBranch,
174+
Map<String, int> original, Map<String, int> current) {
175+
StringBuffer printBuffer = new StringBuffer();
176+
Set<String> quantityChangedOuts = new Set();
177+
Set<String> onlyOriginal = new Set();
178+
Set<String> onlyCurrent = new Set();
179+
Set<String> allKeys =
180+
new Set.from([]..addAll(original.keys)..addAll(current.keys));
181+
182+
for (String key in allKeys) {
183+
if (original.containsKey(key) && !current.containsKey(key)) {
184+
onlyOriginal.add(key);
185+
} else if (!original.containsKey(key) && current.containsKey(key)) {
186+
onlyCurrent.add(key);
187+
} else if (original.containsKey(key) &&
188+
current.containsKey(key) &&
189+
original[key] != current[key]) {
190+
quantityChangedOuts.add(key);
191+
}
192+
}
193+
194+
if (onlyOriginal.isNotEmpty) {
195+
printBuffer.writeln(
196+
'*** $title : ${onlyOriginal.length} warnings from original ($dartdocOriginalBranch) missing in current:');
197+
onlyOriginal.forEach((warning) => printBuffer.writeln(warning));
198+
}
199+
if (onlyCurrent.isNotEmpty) {
200+
printBuffer.writeln(
201+
'*** $title : ${onlyCurrent.length} new warnings not in original ($dartdocOriginalBranch)');
202+
onlyCurrent.forEach((warning) => printBuffer.writeln(warning));
203+
}
204+
if (quantityChangedOuts.isNotEmpty) {
205+
printBuffer.writeln('*** $title : Identical warning quantity changed');
206+
for (String key in quantityChangedOuts) {
207+
printBuffer.writeln(
208+
"* Appeared ${original[key]} times in original ($dartdocOriginalBranch), now ${current[key]}:");
209+
printBuffer.writeln(key);
210+
}
211+
}
212+
if (onlyOriginal.isEmpty &&
213+
onlyCurrent.isEmpty &&
214+
quantityChangedOuts.isEmpty) {
215+
printBuffer.writeln(
216+
'*** $title : No difference in warning output from original ($dartdocOriginalBranch)${allKeys.isEmpty ? "" : " (${allKeys.length} warnings found)"}');
217+
}
218+
return printBuffer.toString();
219+
}

test/io_utils_test.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,36 @@ void main() {
1717
expect(getFileNameFor('dartdoc.generator'), 'dartdoc-generator.html');
1818
});
1919
});
20+
21+
group('printWarningDelta', () {
22+
Map<String, int> original;
23+
Map<String, int> current;
24+
setUp(() {
25+
original = new Map.fromIterables(
26+
["originalwarning", "morewarning", "duplicateoriginalwarning"],
27+
[1, 1, 2]);
28+
current = new Map.fromIterables(
29+
["newwarning", "morewarning", "duplicateoriginalwarning"], [1, 1, 1]);
30+
});
31+
32+
test('verify output of printWarningDelta', () {
33+
expect(
34+
printWarningDelta('Diff Title', 'oldbranch', original, current),
35+
equals(
36+
'*** Diff Title : 1 warnings from original (oldbranch) missing in current:\n'
37+
'originalwarning\n'
38+
'*** Diff Title : 1 new warnings not in original (oldbranch)\n'
39+
'newwarning\n'
40+
'*** Diff Title : Identical warning quantity changed\n'
41+
'* Appeared 2 times in original (oldbranch), now 1:\n'
42+
'duplicateoriginalwarning\n'));
43+
});
44+
45+
test('verify output when nothing changes', () {
46+
expect(
47+
printWarningDelta('Diff Title 2', 'oldbranch2', original, original),
48+
equals(
49+
'*** Diff Title 2 : No difference in warning output from original (oldbranch2) (3 warnings found)\n'));
50+
});
51+
});
2052
}

0 commit comments

Comments
 (0)