runTestSuite method

Future<void> runTestSuite()

Implementation

Future<void> runTestSuite() async {
  try {
    // print('Running LuaLike test suite in $mode mode...');
    print("Files in test suite: $testSuitePath");
    final testDir = Directory(testSuitePath);
    if (!testDir.existsSync()) {
      print('Error: Test suite directory not found: $testSuitePath');
      exit(1);
    }

    // Create logs directory
    final logDir = Directory(logDirPath);
    if (!logDir.existsSync()) {
      logDir.createSync(recursive: true);
    }

    print("Finding test files in $testSuitePath");
    final allTestFiles = await _getTestFiles(testDir);

    // Filter test files based on categories and pattern
    final testFiles = _filterTestFiles(allTestFiles);

    if (testFiles.isEmpty) {
      print('No test files found matching the specified criteria.');
      exit(0);
    }

    print('Found ${testFiles.length} test files to run.');
    progressBar = ProgressBar(total: testFiles.length);

    final suiteStartTime = DateTime.now();
    int passedCount = 0;
    int failedCount = 0;
    int skippedCount = 0;

    if (parallel) {
      print('Running tests in parallel with $parallelJobs jobs...');
      final chunks = _chunkList(testFiles, parallelJobs);
      final futures = chunks.map((chunk) => _runTestChunk(chunk, testDir));

      final results = await Future.wait(futures);

      for (final result in results) {
        passedCount += result['passed'] as int;
        failedCount += result['failed'] as int;
        skippedCount += result['skipped'] as int;
      }
    } else {
      for (final testFile in testFiles) {
        final relativePath = testFile.path.replaceFirst(
          testDir.path + Platform.pathSeparator,
          '',
        );

        // Check if test should be skipped
        if (skipList.contains(relativePath)) {
          if (verbose) {
            print('\n--- Skipping test: $relativePath (in skip list) ---');
          }
          skippedCount++;
          await progressBar.increment();

          results[relativePath] = TestResult(
            name: relativePath,
            passed: true,
            skipped: true,
            duration: Duration.zero,
            category: _getCategoryForTest(relativePath),
          );
          continue;
        }

        if (verbose) {
          print('\n--- Running test: $relativePath ---');
          Logger.setEnabled(true);
        }

        final testStartTime = DateTime.now();
        final logBuffer = StringBuffer();
        logBuffer.writeln('=== Test: $relativePath ===');
        logBuffer.writeln('Start time: $testStartTime');
        // logBuffer.writeln('Mode: $mode');
        logBuffer.writeln(
          'Category: ${_getCategoryForTest(relativePath) ?? "uncategorized"}',
        );

        try {
          final sourceCode = await testFile.readAsString();
          logBuffer.writeln('\n--- Source Code ---');
          logBuffer.writeln(sourceCode);
          logBuffer.writeln('\n--- Execution ---');

          final _ = await executeCode(
            sourceCode,
            // mode,
            onInterpreterSetup: (interpreter) {
              if (useInternalTests) {
                _injectInternalTestFunctions(interpreter.globals);
              }
            },
          );

          final testEndTime = DateTime.now();
          final testDuration = testEndTime.difference(testStartTime);

          logBuffer.writeln('\n--- Result ---');
          logBuffer.writeln('Status: PASSED');
          logBuffer.writeln('Duration: ${testDuration.inMilliseconds} ms');

          if (verbose) {
            print(
              'Test passed: $relativePath (${testDuration.inMilliseconds} ms)',
            );
          }
          passedCount++;

          results[relativePath] = TestResult(
            name: relativePath,
            passed: true,
            duration: testDuration,
            category: _getCategoryForTest(relativePath),
          );
        } catch (e, stackTrace) {
          final testEndTime = DateTime.now();
          final testDuration = testEndTime.difference(testStartTime);

          logBuffer.writeln('\n--- Result ---');
          logBuffer.writeln('Status: FAILED');
          logBuffer.writeln('Error: $e');
          logBuffer.writeln('Stack trace:');
          logBuffer.writeln(stackTrace);
          logBuffer.writeln('Duration: ${testDuration.inMilliseconds} ms');

          if (verbose) {
            print('Test failed: $relativePath');
            print('Error: $e');
          }
          failedCount++;

          results[relativePath] = TestResult(
            name: relativePath,
            passed: false,
            errorMessage: e.toString(),
            duration: testDuration,
            category: _getCategoryForTest(relativePath),
          );
        }

        // Write test log
        final sanitizedName = relativePath.replaceAll(RegExp(r'[\/\\]'), '_');
        final logFile = File('$logDirPath/$sanitizedName.log');
        logFile.writeAsStringSync(logBuffer.toString());

        progressBar.increment();
      }
    }

    final suiteEndTime = DateTime.now();
    final suiteDuration = suiteEndTime.difference(suiteStartTime);

    // Create summary report
    final reportBuffer = StringBuffer();
    reportBuffer.writeln('=== LuaLike Test Suite Summary ===');
    reportBuffer.writeln('Date: $suiteStartTime');
    // reportBuffer.writeln('Mode: $mode');
    reportBuffer.writeln('Total tests: ${testFiles.length}');
    reportBuffer.writeln('Passed: $passedCount');
    reportBuffer.writeln('Failed: $failedCount');
    reportBuffer.writeln('Skipped: $skippedCount');
    reportBuffer.writeln('Duration: ${suiteDuration.inSeconds} seconds');

    // Add category statistics
    reportBuffer.writeln('\n=== Category Statistics ===');
    final categoryStats = _getCategoryStatistics();
    for (final entry in categoryStats.entries) {
      final category = entry.key;
      final stats = entry.value;
      reportBuffer.writeln(
        '$category: ${stats['total']} tests, ${stats['passed']} passed, ${stats['failed']} failed, ${stats['skipped']} skipped',
      );
    }

    reportBuffer.writeln('\n=== Detailed Results ===');

    for (final entry
        in results.entries.toList()..sort((a, b) => a.key.compareTo(b.key))) {
      final result = entry.value;
      if (result.skipped) {
        reportBuffer.writeln('${result.name}: SKIPPED');
      } else {
        reportBuffer.writeln(
          '${result.name}: ${result.passed ? "PASSED" : "FAILED"} (${result.duration.inMilliseconds} ms)',
        );
        if (!result.passed) {
          reportBuffer.writeln('  Error: ${result.errorMessage}');
        }
      }
    }

    // Write summary report
    final reportFile = File('$logDirPath/summary_report.txt');
    reportFile.writeAsStringSync(reportBuffer.toString());

    // Write JSON report for programmatic consumption
    final jsonReport = {
      'timestamp': suiteStartTime.toIso8601String(),
      // 'mode': mode.toString(),
      'totalTests': testFiles.length,
      'passed': passedCount,
      'failed': failedCount,
      'skipped': skippedCount,
      'durationMs': suiteDuration.inMilliseconds,
      'categoryStats': categoryStats,
      'results': results.values.map((r) => r.toJson()).toList(),
    };

    final jsonReportFile = File('$logDirPath/report.json');
    jsonReportFile.writeAsStringSync(jsonEncode(jsonReport));

    print('\n--- Test Results ---');
    print('Total tests: ${testFiles.length}');
    print('Passed: $passedCount');
    print('Failed: $failedCount');
    print('Skipped: $skippedCount');
    print('Duration: ${suiteDuration.inSeconds} seconds');

    // Print category statistics
    print('\n--- Category Statistics ---');
    for (final entry in categoryStats.entries) {
      final category = entry.key;
      final stats = entry.value;
      print(
        '$category: ${stats['total']} tests, ${stats['passed']} passed, ${stats['failed']} failed, ${stats['skipped']} skipped',
      );
    }

    print('\nLog files written to: $logDirPath');
    print('Summary report: ${reportFile.path}');

    if (failedCount > 0) {
      exit(1); // Indicate test failure
    }
  } catch (e, stackTrace) {
    print('\nError running test suite: $e');
    print(stackTrace);
    exit(2);
  }
}