analyzeImpact method
Implementation
ImpactResult analyzeImpact({
required List<String> changedFiles,
Duration analysisDuration = Duration.zero,
bool isGitRepo = true,
bool gitRequiresFallback = false,
String? gitFallbackReason,
bool isConservative = false,
bool forceAll = false,
bool hasAnalysisErrors = false,
String? analysisErrorReason,
}) {
final testPaths = discoveredTests.map((t) => t.path).toList();
final modeStr = forceAll ? 'all' : (isConservative ? 'conservative' : 'default');
List<AffectedTestReason> allTestsAsAffected() {
return testPaths
.map((tp) => AffectedTestReason(
testPath: tp,
dependencyChain: const ['fallback_full_suite'],
))
.toList();
}
const fallbackExplanation =
'Dependency analysis is incomplete. Falling back to the full test suite to avoid false negatives.';
if (forceAll) {
return ImpactResult(
changedFiles: changedFiles,
affectedTests: allTestsAsAffected(),
unaffectedTests: const [],
totalTests: testPaths.length,
analysisDuration: analysisDuration,
status: 'fallback',
mode: modeStr,
isFallback: true,
fallbackReason: 'forced_all',
fallbackExplanation: 'Full test suite requested via --all flag.',
);
}
if (!isGitRepo) {
return ImpactResult(
changedFiles: changedFiles,
affectedTests: allTestsAsAffected(),
unaffectedTests: const [],
totalTests: testPaths.length,
analysisDuration: analysisDuration,
status: 'fallback',
mode: modeStr,
isFallback: true,
fallbackReason: 'no_git_repository',
fallbackExplanation: fallbackExplanation,
);
}
if (gitRequiresFallback) {
return ImpactResult(
changedFiles: changedFiles,
affectedTests: allTestsAsAffected(),
unaffectedTests: const [],
totalTests: testPaths.length,
analysisDuration: analysisDuration,
status: 'fallback',
mode: modeStr,
isFallback: true,
fallbackReason: gitFallbackReason ?? 'project_configuration_changed',
fallbackExplanation: fallbackExplanation,
);
}
if (hasAnalysisErrors) {
return ImpactResult(
changedFiles: changedFiles,
affectedTests: allTestsAsAffected(),
unaffectedTests: const [],
totalTests: testPaths.length,
analysisDuration: analysisDuration,
status: 'fallback',
mode: modeStr,
isFallback: true,
fallbackReason: analysisErrorReason ?? 'analysis_failed',
fallbackExplanation: fallbackExplanation,
);
}
// Check if any non-Dart file is changed in conservative mode
if (isConservative) {
final nonDartChanged = changedFiles.any((f) => !f.endsWith('.dart'));
if (nonDartChanged) {
return ImpactResult(
changedFiles: changedFiles,
affectedTests: allTestsAsAffected(),
unaffectedTests: const [],
totalTests: testPaths.length,
analysisDuration: analysisDuration,
status: 'fallback',
mode: modeStr,
isFallback: true,
fallbackReason: 'conservative_mode',
fallbackExplanation: fallbackExplanation,
);
}
}
// Standard AST reverse DFS traversal
final finder = AffectedTestFinder(graph: graph);
final affected = finder.findAffectedTests(
changedFiles: changedFiles,
testFiles: testPaths,
);
final affectedTestPaths = affected.map((a) => a.testPath).toSet();
final unaffectedTests = testPaths.where((t) => !affectedTestPaths.contains(t)).toList();
unaffectedTests.sort();
return ImpactResult(
changedFiles: changedFiles,
affectedTests: affected,
unaffectedTests: unaffectedTests,
totalTests: testPaths.length,
analysisDuration: analysisDuration,
status: 'safe',
mode: modeStr,
isFallback: false,
fallbackReason: null,
fallbackExplanation: null,
);
}