Found by dogfooding on nuwayze main: `pipe verify` against the ratchet
baseline printed
resolved 1 no longer reported - these fixes are confirmed
+ service_browse_bloc.dart:20 LoadServicesByArtist [dead-handler]
but nothing was fixed. nuwayze commit ccca9bd1e ("delete dead firestore
query code") deleted the event and its handler. Under pipe's default
wire-up policy, deleting an unreachable handler is the exception that
needs approval, so calling it a confirmed fix overstated what happened.
verify now separates the two. A gone finding is REMOVED when its
subject no longer exists:
- its file is not among the project's files;
- for rules labelled by a declaration (the event/state/union/provider or
owner class), that name is no longer declared anywhere;
- for dead-handler, the on<Event> registration itself is gone.
Everything else stays RESOLVED. Member-labelled rules (a method or
build) are classed by file only, never guessed. Removed entries still
count as gone for --expect-resolved; they are only named honestly, in
text ("removed N gone because the code was deleted - not wired up",
a REMOVED section) and JSON (removed, removedEntries).
On nuwayze main the entry now reads REMOVED. Tests: a unit test over
hand-built entries (a wired handler stays resolved, a deleted handler
and a deleted file are removed, a member label is not guessed), and an
end-to-end test that deletes a fixture's dead handler and runs
verifyAnalysis. 216 tests pass.
69 lines
2.1 KiB
Dart
69 lines
2.1 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:pipe/pipe.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
/// A copy of the fixture, so deleting code there cannot touch the real one.
|
|
String _fixtureCopy() {
|
|
final from = Directory('test/fixtures/stuck_loading');
|
|
final to = Directory.systemTemp.createTempSync('pipe-verify-removed');
|
|
for (final entity in from.listSync(recursive: true)) {
|
|
final relative = entity.path.substring(from.path.length + 1);
|
|
final target = '${to.path}/$relative';
|
|
if (entity is Directory) {
|
|
Directory(target).createSync(recursive: true);
|
|
} else if (entity is File) {
|
|
File(target)
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(entity.readAsBytesSync());
|
|
}
|
|
}
|
|
return to.path;
|
|
}
|
|
|
|
void main() {
|
|
test('verify reports a deleted dead handler as removed', () async {
|
|
final root = _fixtureCopy();
|
|
final before = await analyzeProject(
|
|
await loadProject(root),
|
|
concurrency: 1,
|
|
);
|
|
final baseline = decodeBaselineEntries(
|
|
encodeBaseline(root, before.report.findings),
|
|
);
|
|
await before.dispose();
|
|
expect(
|
|
baseline.where(
|
|
(e) => e.rule == ruleDeadHandler && e.label == 'NeverDispatched',
|
|
),
|
|
hasLength(1),
|
|
);
|
|
|
|
// Delete the dead handler instead of wiring it up.
|
|
final gaps = File('$root/lib/gaps.dart');
|
|
gaps.writeAsStringSync(
|
|
gaps.readAsStringSync().replaceFirst(
|
|
'on<NeverDispatched>((event, emit) => emit(const GapIdle()));',
|
|
'',
|
|
),
|
|
);
|
|
|
|
final after = await analyzeProject(await loadProject(root), concurrency: 1);
|
|
try {
|
|
final verification = verifyAnalysis(analysis: after, baseline: baseline);
|
|
final json = jsonDecode(jsonEncode(verification.toJson())) as Map;
|
|
expect(
|
|
verification.removed.map((e) => e.label),
|
|
contains('NeverDispatched'),
|
|
);
|
|
expect(
|
|
verification.resolved.map((e) => e.label),
|
|
isNot(contains('NeverDispatched')),
|
|
);
|
|
expect(json['removed'], greaterThanOrEqualTo(1));
|
|
} finally {
|
|
await after.dispose();
|
|
}
|
|
});
|
|
}
|