import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:path/path.dart' as path; import 'package:lanebow/utils/test_config.dart'; /// 테스트 진단 및 문제 해결을 위한 유틸리티 클래스 class TestDiagnostics { static TestDiagnostics? _instance; // 진단 로그 저장 경로 final String _logDir; // 진단 로그 파일 경로 final String _logFilePath; // 진단 로그 파일 IOSink? _logSink; // 테스트 실패 카운트 int _failureCount = 0; // 테스트 경고 카운트 int _warningCount = 0; // 시작 시간 final DateTime _startTime = DateTime.now(); /// 싱글톤 인스턴스 반환 static TestDiagnostics get instance { _instance ??= TestDiagnostics._internal(); return _instance!; } TestDiagnostics._internal() : _logDir = path.join(Directory.current.path, 'test_diagnostics'), _logFilePath = path.join( Directory.current.path, 'test_diagnostics', 'test_diagnostics_${DateTime.now().millisecondsSinceEpoch}.log' ) { _initializeLogDir(); } /// 로그 디렉토리 초기화 void _initializeLogDir() { try { final dir = Directory(_logDir); if (!dir.existsSync()) { dir.createSync(recursive: true); } // 로그 파일 생성 _logSink = File(_logFilePath).openWrite(mode: FileMode.writeOnly); _log('테스트 진단 시작: ${DateTime.now()}'); _log('환경 정보: ${Platform.operatingSystem} ${Platform.operatingSystemVersion}'); _log('테스트 설정: asyncTimeout=${TestConfig.instance.asyncTimeout}ms, testDelay=${TestConfig.instance.testDelay}ms'); } catch (e) { if (kDebugMode) { print('TestDiagnostics: 로그 디렉토리 초기화 중 오류 - $e'); } } } /// 로그 기록 void _log(String message) { final timestamp = DateTime.now().toIso8601String(); final logMessage = '[$timestamp] $message'; _logSink?.writeln(logMessage); if (kDebugMode) { print('TestDiagnostics: $message'); } } /// 테스트 실패 기록 void recordFailure(String testName, String errorMessage, {StackTrace? stackTrace}) { _failureCount++; _log('테스트 실패 #$_failureCount: $testName'); _log('오류 메시지: $errorMessage'); if (stackTrace != null) { _log('스택 트레이스:\n$stackTrace'); } // 시스템 정보 수집 _collectSystemInfo(); } /// 테스트 경고 기록 void recordWarning(String testName, String warningMessage) { _warningCount++; _log('테스트 경고 #$_warningCount: $testName'); _log('경고 메시지: $warningMessage'); } /// 시스템 정보 수집 Future _collectSystemInfo() async { try { _log('--- 시스템 정보 수집 시작 ---'); // 메모리 사용량 정보 수집 (필요시 구현) // 프로세스 정보 (플랫폼별 처리) if (Platform.isLinux || Platform.isMacOS) { try { final result = await Process.run('ps', ['-o', 'pid,ppid,rss,vsz,pcpu,pmem,command', '-p', '${currentPid}']); _log('프로세스 정보:\n${result.stdout}'); } catch (e) { _log('프로세스 정보 수집 실패: $e'); } } // 실행 중인 테스트 프로세스 목록 try { final result = await Process.run('ps', ['aux', '|', 'grep', 'flutter test']); _log('테스트 프로세스 목록:\n${result.stdout}'); } catch (e) { _log('테스트 프로세스 목록 수집 실패: $e'); } // 시스템 부하 정보 if (Platform.isLinux || Platform.isMacOS) { try { final result = await Process.run('uptime', []); _log('시스템 부하 정보:\n${result.stdout}'); } catch (e) { _log('시스템 부하 정보 수집 실패: $e'); } } _log('--- 시스템 정보 수집 완료 ---'); } catch (e) { _log('시스템 정보 수집 중 오류: $e'); } } /// 테스트 진단 요약 생성 Future generateSummary() async { final duration = DateTime.now().difference(_startTime); final summary = StringBuffer(); summary.writeln('=== 테스트 진단 요약 ==='); summary.writeln('실행 시간: ${duration.inSeconds}초'); summary.writeln('실패 수: $_failureCount'); summary.writeln('경고 수: $_warningCount'); summary.writeln('테스트 설정:'); summary.writeln(' - asyncTimeout: ${TestConfig.instance.asyncTimeout}ms'); summary.writeln(' - testDelay: ${TestConfig.instance.testDelay}ms'); summary.writeln(' - retryCount: ${TestConfig.instance.retryCount}'); summary.writeln(' - isolationMode: ${TestConfig.instance.isolationMode}'); summary.writeln(' - CI 환경: ${TestConfig.instance.isCI}'); final summaryText = summary.toString(); _log(summaryText); return summaryText; } /// 테스트 진단 종료 Future close() async { await generateSummary(); _log('테스트 진단 종료: ${DateTime.now()}'); await _logSink?.flush(); await _logSink?.close(); _logSink = null; if (kDebugMode) { print('TestDiagnostics: 진단 로그 저장됨 - $_logFilePath'); } } /// 테스트 실패 시 최소 재현 스크립트 생성 Future generateMinimalReproductionScript(String testName) async { final scriptPath = path.join(_logDir, 'reproduce_${testName.replaceAll(RegExp(r'[^\w]'), '_')}.sh'); final script = File(scriptPath); final buffer = StringBuffer(); buffer.writeln('#!/bin/bash'); buffer.writeln('# $testName 테스트 실패 재현 스크립트'); buffer.writeln('# 생성일: ${DateTime.now()}'); buffer.writeln(''); buffer.writeln('# 환경 변수 설정'); buffer.writeln('export FLUTTER_TEST_TIMEOUT_OVERRIDE=${TestConfig.instance.asyncTimeout}'); buffer.writeln('export FLUTTER_TEST_RETRY_COUNT=${TestConfig.instance.retryCount}'); buffer.writeln(''); buffer.writeln('# 테스트 실행'); buffer.writeln('cd "\$(dirname "\$0")/.."'); buffer.writeln('flutter test --name="$testName" --verbose'); await script.writeAsString(buffer.toString()); // 실행 권한 부여 if (Platform.isLinux || Platform.isMacOS) { await Process.run('chmod', ['+x', scriptPath]); } _log('재현 스크립트 생성됨: $scriptPath'); return scriptPath; } /// 현재 프로세스 ID static int get currentPid => pid; }