Skip to content

Commit 5967e93

Browse files
committed
[Backend Tester] Add markdown summary in CI
ghstack-source-id: 26a3832 ghstack-comment-id: 3203649834 Pull-Request: #13535
1 parent 48bfad7 commit 5967e93

File tree

4 files changed

+122
-4
lines changed

4 files changed

+122
-4
lines changed

.ci/scripts/test_backend_linux.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ SUITE=$1
1010
FLOW=$2
1111
ARTIFACT_DIR=$3
1212

13+
REPORT_FILE="$ARTIFACT_DIR/test-report-$FLOW-$SUITE.csv"
14+
1315
echo "Running backend test job for suite $SUITE, flow $FLOW."
1416
echo "Saving job artifacts to $ARTIFACT_DIR."
1517

@@ -27,4 +29,7 @@ EXTRA_BUILD_ARGS="-DEXECUTORCH_BUILD_VULKAN=ON"
2729
# We need the runner to test the built library.
2830
PYTHON_EXECUTABLE=python CMAKE_ARGS="$EXTRA_BUILD_ARGS" .ci/scripts/setup-linux.sh --build-tool cmake --build-mode Release --editable true
2931

30-
python -m executorch.backends.test.suite.runner $SUITE --flow $FLOW --report "$ARTIFACT_DIR/test_results.csv"
32+
python -m executorch.backends.test.suite.runner $SUITE --flow $FLOW --report "$REPORT_FILE"
33+
34+
# Generate markdown summary.
35+
python -m executorch.backends.test.suite.generate_markdown_summary "$REPORT_FILE" > ${GITHUB_STEP_SUMMARY:step_summary.md}

.ci/scripts/test_backend_macos.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ SUITE=$1
1010
FLOW=$2
1111
ARTIFACT_DIR=$3
1212

13+
REPORT_FILE="$ARTIFACT_DIR/test-report-$FLOW-$SUITE.csv"
14+
1315
echo "Running backend test job for suite $SUITE, flow $FLOW."
1416
echo "Saving job artifacts to $ARTIFACT_DIR."
1517

@@ -21,4 +23,7 @@ eval "$(conda shell.bash hook)"
2123
PYTHON_EXECUTABLE=python
2224
${CONDA_RUN} --no-capture-output .ci/scripts/setup-macos.sh --build-tool cmake --build-mode Release
2325

24-
${CONDA_RUN} --no-capture-output python -m executorch.backends.test.suite.runner $SUITE --flow $FLOW --report "$ARTIFACT_DIR/test_results.csv"
26+
${CONDA_RUN} --no-capture-output python -m executorch.backends.test.suite.runner $SUITE --flow $FLOW --report "$REPORT_FILE"
27+
28+
# Generate markdown summary.
29+
${CONDA_RUN} --no-capture-output python -m executorch.backends.test.suite.generate_markdown_summary "$REPORT_FILE" > ${GITHUB_STEP_SUMMARY:step_summary.md}

.github/workflows/nightly.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
# Intentionally suppressing exit code for now.
6161
# TODO (gjcomer) Remove this when jobs are stable.
6262
EXIT_CODE=0
63-
.ci/scripts/test_backend_linux.sh "${{ matrix.suite }}" "${{ matrix.flow }}" "${RUNNER_ARTIFACT_DIR}" || EXIT_CODE=$?
63+
source .ci/scripts/test_backend_linux.sh "${{ matrix.suite }}" "${{ matrix.flow }}" "${RUNNER_ARTIFACT_DIR}" || EXIT_CODE=$?
6464
echo "Test run complete with exit code $EXIT_CODE."
6565
6666
backend-test-macos:
@@ -87,5 +87,5 @@ jobs:
8787
${CONDA_RUN} --no-capture-output pip install awscli==1.37.21
8888
8989
EXIT_CODE=0
90-
.ci/scripts/test_backend_macos.sh "${{ matrix.suite }}" "${{ matrix.flow }}" "${RUNNER_ARTIFACT_DIR}" || EXIT_CODE=$?
90+
source .ci/scripts/test_backend_macos.sh "${{ matrix.suite }}" "${{ matrix.flow }}" "${RUNNER_ARTIFACT_DIR}" || EXIT_CODE=$?
9191
echo "Test run complete with exit code $EXIT_CODE."
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import argparse
2+
import csv
3+
import sys
4+
5+
#
6+
# A standalone script to generate a Markdown representation of a test report.
7+
# This is primarily intended to be used with GitHub actions to generate a nice
8+
# representation of the test results when looking at the action run.
9+
#
10+
# Usage: python executorch/backends/test/suite/generate_markdown_summary.py <path to test report CSV file>
11+
# Markdown is written to stdout.
12+
#
13+
14+
def generate_markdown(csv_path: str):
15+
with open(csv_path, newline='', encoding='utf-8') as f:
16+
reader = csv.reader(f)
17+
rows = list(reader)
18+
19+
header = rows[0]
20+
data_rows = rows[1:]
21+
22+
# Find the Result and Result Detail column indices
23+
result_column_index = None
24+
result_detail_column_index = None
25+
for i, col in enumerate(header):
26+
if col.lower() == 'result':
27+
result_column_index = i
28+
elif col.lower() == 'result detail':
29+
result_detail_column_index = i
30+
31+
# Count results and prepare data
32+
pass_count = 0
33+
fail_count = 0
34+
skip_count = 0
35+
failed_tests = []
36+
processed_rows = []
37+
result_detail_counts = {}
38+
39+
for row in data_rows:
40+
# Make a copy of the row to avoid modifying the original
41+
processed_row = row.copy()
42+
43+
# Count results and collect failed tests
44+
if result_column_index is not None and result_column_index < len(row):
45+
result_value = row[result_column_index].strip().lower()
46+
if result_value == 'pass':
47+
pass_count += 1
48+
processed_row[result_column_index] = '<span style="color:green">Pass</span>'
49+
elif result_value == 'fail':
50+
fail_count += 1
51+
processed_row[result_column_index] = '<span style="color:red">Fail</span>'
52+
failed_tests.append(processed_row.copy())
53+
elif result_value == 'skip':
54+
skip_count += 1
55+
processed_row[result_column_index] = '<span style="color:gray">Skip</span>'
56+
57+
# Count result details (excluding empty ones)
58+
if result_detail_column_index is not None and result_detail_column_index < len(row):
59+
result_detail_value = row[result_detail_column_index].strip()
60+
if result_detail_value: # Only count non-empty result details
61+
if result_detail_value in result_detail_counts:
62+
result_detail_counts[result_detail_value] += 1
63+
else:
64+
result_detail_counts[result_detail_value] = 1
65+
66+
processed_rows.append(processed_row)
67+
68+
# Generate Summary section
69+
total_rows = len(data_rows)
70+
print("# Summary\n")
71+
print(f"- **Pass**: {pass_count}/{total_rows}")
72+
print(f"- **Fail**: {fail_count}/{total_rows}")
73+
print(f"- **Skip**: {skip_count}/{total_rows}")
74+
75+
print("## Failure Breakdown:")
76+
total_rows_with_result_detail = sum(result_detail_counts.values())
77+
for detail, count in sorted(result_detail_counts.items()):
78+
print(f"- **{detail}**: {count}/{total_rows_with_result_detail}")
79+
80+
# Generate Failed Tests section
81+
print("# Failed Tests\n")
82+
if failed_tests:
83+
print("| " + " | ".join(header) + " |")
84+
print("|" + "|".join(['---'] * len(header)) + "|")
85+
for row in failed_tests:
86+
print("| " + " | ".join(row) + " |")
87+
else:
88+
print("No failed tests.\n")
89+
print()
90+
91+
# Generate Test Cases section
92+
print("# Test Cases\n")
93+
print("| " + " | ".join(header) + " |")
94+
print("|" + "|".join(['---'] * len(header)) + "|")
95+
for row in processed_rows:
96+
print("| " + " | ".join(row) + " |")
97+
98+
def main():
99+
parser = argparse.ArgumentParser(description="Generate a Markdown representation of a test report.")
100+
parser.add_argument("csv_path", help="Path to the test report CSV file.")
101+
args = parser.parse_args()
102+
try:
103+
generate_markdown(args.csv_path)
104+
except Exception as e:
105+
print(f"Error: {e}", file=sys.stderr)
106+
sys.exit(1)
107+
if __name__ == "__main__":
108+
main()

0 commit comments

Comments
 (0)