Files
hartmann-foto_documentation/hartmann-foto-documentation-frontend/test_runner.dart
2026-01-21 16:08:09 +01:00

133 lines
4.1 KiB
Dart

#!/usr/bin/env dart
import 'dart:convert';
import 'dart:io';
import 'package:logger/web.dart';
/// Converts Flutter JSON test results to JUnit XML format
/// Usage: dart test_runner.dart [test_results.json] [output.xml]
void main(List<String> arguments) {
var logger = Logger(
printer: PrettyPrinter(methodCount: 2, errorMethodCount: 8, colors: true, printEmojis: true, dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart),
);
if (arguments.length < 2) {
logger.i('Usage: dart test_runner.dart <json_file> <xml_output>');
exit(1);
}
final jsonFile = arguments[0];
final xmlFile = arguments[1];
try {
final jsonContent = File(jsonFile).readAsStringSync();
final lines = jsonContent.trim().split('\n');
final tests = <TestResult>[];
int totalTests = 0;
int failures = 0;
int skipped = 0;
double duration = 0.0;
// Parse JSON lines from flutter test output
for (final line in lines) {
if (line.trim().isEmpty) continue;
try {
final data = jsonDecode(line) as Map<String, dynamic>;
final type = data['type'] as String?;
if (type == 'testStart') {
final test = data['test'] as Map<String, dynamic>;
final name = test['name'] as String;
final id = test['id'] as int;
tests.add(TestResult(id: id, name: name));
totalTests++;
} else if (type == 'testDone') {
final testId = data['testID'] as int;
final result = data['result'] as String;
final time = data['time'] as int? ?? 0;
final error = data['error'] as String?;
final stackTrace = data['stackTrace'] as String?;
duration += time / 1000.0; // Convert to seconds
final testIndex = tests.indexWhere((t) => t.id == testId);
if (testIndex != -1) {
tests[testIndex].result = result;
tests[testIndex].duration = time / 1000.0;
tests[testIndex].error = error;
tests[testIndex].stackTrace = stackTrace;
if (result == 'error' || result == 'failure') {
failures++;
} else if (result == 'skip') {
skipped++;
}
}
}
} catch (e) {
// Skip malformed JSON lines
logger.i('Warning: Could not parse line: $line');
}
}
// Generate JUnit XML
final xml = generateJUnitXML(tests, totalTests, failures, skipped, duration);
File(xmlFile).writeAsStringSync(xml);
logger.i('Converted ${tests.length} test results to JUnit XML: $xmlFile');
// Exit with error code if there were failures
if (failures > 0) {
exit(1);
}
} catch (e) {
logger.e('Error: $e');
exit(1);
}
}
class TestResult {
final int id;
final String name;
String result = 'pending';
double duration = 0.0;
String? error;
String? stackTrace;
TestResult({required this.id, required this.name});
}
String generateJUnitXML(List<TestResult> tests, int total, int failures, int skipped, double duration) {
final buffer = StringBuffer();
buffer.writeln('<?xml version="1.0" encoding="UTF-8"?>');
buffer.writeln('<testsuite name="Flutter Tests" tests="$total" failures="$failures" skipped="$skipped" time="$duration">');
for (final test in tests) {
final escapedName = _escapeXml(test.name);
buffer.writeln(' <testcase name="$escapedName" time="${test.duration}">');
if (test.result == 'error' || test.result == 'failure') {
final escapedError = _escapeXml(test.error ?? 'Test failed');
final escapedStackTrace = _escapeXml(test.stackTrace ?? '');
buffer.writeln(' <failure message="$escapedError">');
buffer.writeln(' <![CDATA[$escapedStackTrace]]>');
buffer.writeln(' </failure>');
} else if (test.result == 'skip') {
buffer.writeln(' <skipped/>');
}
buffer.writeln(' </testcase>');
}
buffer.writeln('</testsuite>');
return buffer.toString();
}
String _escapeXml(String text) {
return text.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&apos;');
}