Skip to content

Commit 51588d3

Browse files
committed
better junit
1 parent b17e4b9 commit 51588d3

3 files changed

Lines changed: 109 additions & 7 deletions

File tree

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ jobs:
7171
uses: codecov/test-results-action@v1
7272
with:
7373
files: ./junit.xml
74+
flags: libbin-tests
7475
token: ${{ secrets.CODECOV_TOKEN }}
7576

7677
memory-check:
@@ -128,11 +129,25 @@ jobs:
128129
run: |
129130
make check
130131
132+
- name: Debug - Check if junit.xml exists
133+
run: |
134+
if [ -f junit.xml ]; then
135+
echo "✅ junit.xml exists"
136+
ls -la junit.xml
137+
echo "=== First 10 lines of junit.xml ==="
138+
head -10 junit.xml
139+
else
140+
echo "❌ junit.xml not found"
141+
echo "Files in current directory:"
142+
ls -la
143+
fi
144+
131145
- name: Upload test results to Codecov
132146
if: ${{ !cancelled() }}
133147
uses: codecov/test-results-action@v1
134148
with:
135149
files: ./junit.xml
150+
flags: libbin-tests
136151
token: ${{ secrets.CODECOV_TOKEN }}
137152

138153
lint:

test/bin_tests_debug.c

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,51 @@
22
#include <stdio.h>
33
#include <time.h>
44
#include <string.h>
5+
#ifdef __unix__
6+
#include <unistd.h>
7+
#endif
58

69
// JUnit XML state tracking
710
#define MAX_TESTS 100
811
static struct {
912
char name[64];
1013
int passed;
14+
double duration; // Test execution time in seconds
1115
} test_results[MAX_TESTS];
1216
static int test_count = 0;
1317
static time_t start_time;
1418
static int junit_enabled = 0;
19+
static clock_t current_test_start;
1520

1621
// Initialize JUnit XML output
1722
void initJunitXml(void) {
1823
junit_enabled = 1;
1924
test_count = 0;
2025
start_time = time(NULL);
26+
current_test_start = 0;
27+
}
28+
29+
// Start timing a test case
30+
void startTestTiming(void) {
31+
if (!junit_enabled) return;
32+
current_test_start = clock();
2133
}
2234

2335
// Record a test result for JUnit XML output
2436
void recordJunitTestResult(const char* testName, bin_int_t results) {
2537
if (!junit_enabled || test_count >= MAX_TESTS) return;
2638

39+
// Calculate test duration
40+
double duration = 0.0;
41+
if (current_test_start != 0) {
42+
clock_t end_time = clock();
43+
duration = ((double)(end_time - current_test_start)) / CLOCKS_PER_SEC;
44+
}
45+
2746
strncpy(test_results[test_count].name, testName, sizeof(test_results[test_count].name) - 1);
2847
test_results[test_count].name[sizeof(test_results[test_count].name) - 1] = '\0';
2948
test_results[test_count].passed = results;
49+
test_results[test_count].duration = duration;
3050
test_count++;
3151
}
3252

@@ -50,16 +70,78 @@ void finalizeJunitXml(void) {
5070

5171
fprintf(junit_file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
5272
fprintf(junit_file, "<testsuites>\n");
53-
fprintf(junit_file, " <testsuite name=\"libbin_tests\" tests=\"%d\" failures=\"%d\" time=\"%.3f\" timestamp=\"%ld\">\n",
54-
test_count, failure_count, duration, (long)start_time);
73+
74+
// Get hostname
75+
char hostname[256] = "unknown";
76+
#ifdef __unix__
77+
if (gethostname(hostname, sizeof(hostname)) != 0) {
78+
strcpy(hostname, "unix-host");
79+
}
80+
#elif defined(_WIN32)
81+
strcpy(hostname, "windows-host");
82+
#else
83+
strcpy(hostname, "unknown-host");
84+
#endif
85+
86+
// Format ISO 8601 timestamp
87+
char iso_timestamp[32];
88+
struct tm* tm_info = gmtime(&start_time);
89+
strftime(iso_timestamp, sizeof(iso_timestamp), "%Y-%m-%dT%H:%M:%SZ", tm_info);
90+
91+
fprintf(junit_file, " <testsuite name=\"libbin_tests\" tests=\"%d\" failures=\"%d\" errors=\"0\" skipped=\"0\" time=\"%.3f\" timestamp=\"%s\" hostname=\"%s\">\n",
92+
test_count, failure_count, duration, iso_timestamp, hostname);
93+
94+
// Add properties section with environment info
95+
fprintf(junit_file, " <properties>\n");
96+
fprintf(junit_file, " <property name=\"test.framework\" value=\"libbin-custom\"/>\n");
97+
fprintf(junit_file, " <property name=\"test.language\" value=\"C\"/>\n");
98+
fprintf(junit_file, " <property name=\"test.standard\" value=\"c2x\"/>\n");
99+
fprintf(junit_file, " <property name=\"test.compiler\" value=\"clang\"/>\n");
100+
#ifdef __VERSION__
101+
fprintf(junit_file, " <property name=\"test.compiler.version\" value=\"%s\"/>\n", __VERSION__);
102+
#endif
103+
#ifdef BIN_BITS
104+
fprintf(junit_file, " <property name=\"libbin.bits\" value=\"%d\"/>\n", BIN_BITS);
105+
#endif
106+
#ifdef BIN_INT_MAX
107+
fprintf(junit_file, " <property name=\"libbin.int_max\" value=\"%d\"/>\n", BIN_INT_MAX);
108+
#endif
109+
fprintf(junit_file, " <property name=\"test.date\" value=\"%s\"/>\n", iso_timestamp);
110+
fprintf(junit_file, " </properties>\n");
55111

56112
for (int i = 0; i < test_count; i++) {
57-
fprintf(junit_file, " <testcase name=\"%s\" classname=\"libbin\"", test_results[i].name);
113+
// Determine test category from name
114+
const char* category = "misc";
115+
if (strstr(test_results[i].name, "bin") == test_results[i].name) {
116+
if (strstr(test_results[i].name, "MSB") || strstr(test_results[i].name, "LSB") ||
117+
strstr(test_results[i].name, "Shift") || strstr(test_results[i].name, "AND") ||
118+
strstr(test_results[i].name, "OR") || strstr(test_results[i].name, "XOR") ||
119+
strstr(test_results[i].name, "NOT") || strstr(test_results[i].name, "Rotate")) {
120+
category = "bitwise";
121+
} else if (strstr(test_results[i].name, "Add") || strstr(test_results[i].name, "Sub") ||
122+
strstr(test_results[i].name, "Mult") || strstr(test_results[i].name, "Div") ||
123+
strstr(test_results[i].name, "Mod") || strstr(test_results[i].name, "Pow") ||
124+
strstr(test_results[i].name, "Sqrt") || strstr(test_results[i].name, "Log")) {
125+
category = "math";
126+
} else if (strstr(test_results[i].name, "EQ") || strstr(test_results[i].name, "GT") ||
127+
strstr(test_results[i].name, "LT")) {
128+
category = "boolean";
129+
}
130+
}
131+
132+
fprintf(junit_file, " <testcase name=\"%s\" classname=\"libbin.%s\" time=\"%.6f\"",
133+
test_results[i].name, category, test_results[i].duration);
134+
58135
if (test_results[i].passed) {
59-
fprintf(junit_file, "/>\n");
136+
fprintf(junit_file, ">\n");
137+
fprintf(junit_file, " <system-out><![CDATA[Test passed successfully]]></system-out>\n");
138+
fprintf(junit_file, " </testcase>\n");
60139
} else {
61140
fprintf(junit_file, ">\n");
62-
fprintf(junit_file, " <failure message=\"Test failed\" type=\"AssertionError\"></failure>\n");
141+
fprintf(junit_file, " <failure message=\"Test assertion failed\" type=\"AssertionError\">\n");
142+
fprintf(junit_file, " <![CDATA[Test '%s' failed assertion checks]]>\n", test_results[i].name);
143+
fprintf(junit_file, " </failure>\n");
144+
fprintf(junit_file, " <system-err><![CDATA[Test failed - check implementation]]></system-err>\n");
63145
fprintf(junit_file, " </testcase>\n");
64146
}
65147
}
@@ -79,6 +161,6 @@ void processTestResults(const char* testName, const bin_int_t results) {
79161
printf(format, testName, color, text, CC_RESET);
80162
fflush(stdout);
81163

82-
// Also record for JUnit XML
164+
// Also record for JUnit XML (timing already started before test execution)
83165
recordJunitTestResult(testName, results);
84166
}

test/bin_tests_debug.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ void processTestResults(const char* testName, bin_int_t results);
2121
void initJunitXml(void);
2222

2323
/**
24-
* @brief Record a test result for JUnit XML output
24+
* @brief Start timing a test case
25+
*/
26+
void startTestTiming(void);
27+
28+
/**
29+
* @brief Record a test result for JUnit XML output with timing
2530
* @param testName The name of the test
2631
* @param results Whether the test passed (1) or failed (0)
2732
*/

0 commit comments

Comments
 (0)