import 'dart:io'; import 'package:pipe/pipe.dart'; import 'package:test/test.dart'; /// The fixture copied with [padding] blank lines inserted at the top of every /// Dart source, so every finding moves down without any semantic change. String _shiftedFixture(int padding) { final from = Directory('test/fixtures/stuck_loading'); final to = Directory.systemTemp.createTempSync('pipe-shifted'); 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); if (entity.path.endsWith('.dart')) { // After any `library;` / import header would change nothing; a comment // block at the very top shifts every line in the file. File(target).writeAsStringSync( '${List.filled(padding, '//').join('\n')}\n' '${entity.readAsStringSync()}', ); } else { File(target).writeAsBytesSync(entity.readAsBytesSync()); } } } return to.path; } Future> _findings(String root) async { final analysis = await analyzeProject( await loadProject(root), concurrency: 1, ); try { return analysis.report.findings; } finally { await analysis.dispose(); } } void main() { late List original; late List shifted; setUpAll(() async { original = await _findings('test/fixtures/stuck_loading'); shifted = await _findings(_shiftedFixture(7)); }); test('moving code down a file does not change any fingerprint', () { // A baseline must keep accepting a finding when unrelated edits move it. // Any fingerprint carrying a line number resurrected accepted findings as // new on the next check. expect( shifted.map((f) => f.line), isNot(equals(original.map((f) => f.line))), reason: 'the control must actually move the findings', ); final before = {for (final f in original) f.fingerprint: f.rule}; final after = {for (final f in shifted) f.fingerprint: f.rule}; final moved = { for (final entry in before.entries) if (!after.containsKey(entry.key)) entry.key: entry.value, }; expect(moved, isEmpty, reason: 'fingerprints that depend on position'); }); test('no two findings share a fingerprint', () { // A collision makes the baseline keep one finding and silently drop the // other, so a second real defect can never be reported as new. final counts = {}; for (final f in original) { counts[f.fingerprint] = (counts[f.fingerprint] ?? 0) + 1; } expect({ for (final e in counts.entries) if (e.value > 1) e.key: e.value, }, isEmpty); }); }