analyzeImpact method

ImpactResult analyzeImpact({
  1. required List<String> changedFiles,
  2. Duration analysisDuration = Duration.zero,
  3. bool isGitRepo = true,
  4. bool gitRequiresFallback = false,
  5. String? gitFallbackReason,
  6. bool isConservative = false,
  7. bool forceAll = false,
  8. bool hasAnalysisErrors = false,
  9. String? analysisErrorReason,
})

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,
  );
}