Skip to content

Commit

Permalink
Report test result to comment on PR
Browse files Browse the repository at this point in the history
  • Loading branch information
JackyWoo committed Sep 19, 2024
1 parent 853a78e commit 33c6e89
Show file tree
Hide file tree
Showing 15 changed files with 591 additions and 100 deletions.
48 changes: 48 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Build

on: # yamllint disable-line rule:truthy
workflow_call:
inputs:
build_type:
required: true
type: string
sanitize:
required: true
type: string

jobs:
run:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Prepare environment
run: bash .github/workflows/prepare-env.sh 14

- name: Generate Makefile
run: |
export CC=`which clang` CXX=`which clang++`
SANITIZE_OPTION=$(python3 .github/workflows/translate_sanitize_to_ck_build_option.py ${{ inputs.sanitize }})
if [ "$SANITIZE_OPTION" != "none" ]; then
cmake -G Ninja -B ./build -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DSANITIZE=$SANITIZE_OPTION
else
cmake -G Ninja -B ./build -DCMAKE_BUILD_TYPE=${{ inputs.build_type }}
fi
- name: Build
working-directory: ${{ github.workspace }}/build
run: ninja -j 10

- name: Upload RaftKeeper binary
uses: actions/upload-artifact@v4
with:
name: raftkeeper-binary-${{ inputs.sanitize}}
path: build/programs/raftkeeper

- name: Upload unit test binary
uses: actions/upload-artifact@v4
with:
name: unit-test-binary-${{ inputs.sanitize}}
path: build/src/rk_unit_tests
20 changes: 20 additions & 0 deletions .github/workflows/check-style.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Check Style

on: # yamllint disable-line rule:truthy
workflow_call:

env:
REPORT_DIR: /tmp/reports

jobs:
run:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3

- name: Install tools
run: sudo apt-get update && sudo apt install -y shellcheck libxml2-utils git python3-pip pylint yamllint && pip3 install codespell

- name: Check style
working-directory: ${{ github.workspace }}/tests/ci
run: python3 code_style_check.py | tee style-report.log
76 changes: 76 additions & 0 deletions .github/workflows/generate_test_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import xml.etree.ElementTree as ET

import os


def generate_report(report_dir, report_title):
print("report_dir:", report_dir)
test_cases = {}

# Iterate over all files in the directory
for report_file in os.listdir(report_dir):
print("report_file:", report_file)
if report_file.endswith(('-none.xml', '-tsan.xml', '-msan.xml', '-asan.xml', '-ubsan.xml')):
sanitize_type = report_file.split('-')[-1].split('.')[0]
with open(os.path.join(report_dir, report_file), 'r') as file:
xml_data = file.read()
print("xml_data:", xml_data)
root = ET.fromstring(xml_data)

for testsuite in root.findall('testsuite'):
for testcase in testsuite.findall('testcase'):
# integration test classname is like test_auto.test, we only need the first part
classname = testcase.get('classname').split('.')[0]
name = testcase.get('name')
failure = testcase.find('failure')
status = '❌' if failure is not None else '✅'
error_message = failure.get('message').replace('\n', '<br>') if failure is not None else ''

print(f"test case: {classname} {name} {failure} {error_message} {status}")
if (classname, name) not in test_cases:
test_cases[(classname, name)] = []

test_cases[(classname, name)].append((sanitize_type, status, error_message))

header = "| Classname | Name | Sanitize Type | Status | Error Message |\n"
header += "|---------------|----------------------------------------|---------------|--------|---------------|\n"

successful_tests = []
failed_tests = []

for (classname, name), results in test_cases.items():
# If all of test case are passed or not passed, squash them into one row and set sanitize_type to '-'
statuses = {status for _, status, _ in results}
if len(statuses) == 1:
status = statuses.pop()
# If there's an error message, use it; otherwise, set it to an empty string
error_message = next((error for _, _, error in results if error), '')
row = f"| {classname} | {name} | | {status} | {error_message} |"
if error_message != '':
failed_tests.append(row)
else:
successful_tests.append(row)
else:
# Process normally if not squashed
for sanitize_type, status, error_message in results:
row = f"| {classname} | {name} | {sanitize_type} | {status} | {error_message} |"
if error_message != '':
failed_tests.append(row)
else:
successful_tests.append(row)

failed_table = header + "\n".join(failed_tests) if failed_tests else "All test cases passed!"
successful_table = header + "\n".join(successful_tests) if successful_tests else ""

collapsible_successful_table = (
"<details>\n"
"<summary>Successful Test Cases</summary>\n\n"
+ successful_table +
"\n</details>"
)

commit_id = os.environ['GITHUB_SHA']
print("commit_id:", commit_id)

report = f"{report_title} for commit {commit_id}.\n\n{failed_table}\n\n{collapsible_successful_table}"
return report
6 changes: 6 additions & 0 deletions .github/workflows/get_artifact_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from github_utils import get_artifact_url
import sys


if __name__ == "__main__":
get_artifact_url(sys.argv[1])
107 changes: 107 additions & 0 deletions .github/workflows/github_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import os
import sys
import json

from github.Auth import AppAuth
from github import Github


app_id = "995720"
installation_id = 54780428

private_key_part_1 = """-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAyN+CJniZ5AXyAdcHmfwq0ltaaSLBahjsndlJxJY4zxS3ywxC
"""

private_key_part_2 = """ornAzDEKp4VtBf9EWIraMsCk1Ak9Wp0yICNngJ+Ch13173xk2nkRNBRYAnq+Ln+x
3VetS7hNMkHrSRHZezapzMCki9C0E7a9ks2F3sMwj8rKKhCvspMWGDBTMan5pKkc
YLlOhA27MF66SOIdDAqbi9m0pKeoNvaFaD5hRykYE+bDjnfTXSRWzPragEk5pbKl
sROzSDHqNYDH1d3QK4D1Uk7uT5ZQnkQuWFfliLbSaqPk4NhRwQx4jCqbkQZ4HvPm
8q8I5fql1gDwOxPr1eTLOU00C5x3wcwnFT+MXwIDAQABAoIBAC38FNRvyXMM9WD8
c+4Jb1gmt6TX4wVB3XEpXBzX8vtdF9Iw5VRRR9S26WR+Q/4aeO/4IYl61oD/+H60
+9Olpz0nxv7sQK/pf0EQdCLDAX7X0I/ehb5RIwfxkiKOOqnIn0v4sJiCBWlIhuD4
dZ+U0y+y6XwRhYRpu38a8vToozL7+WX/Wu656FK0H0huH53EazrxxD6JLpoIFErK
ojU5vzPJUgTdWnMZAdlEOjcO0Qg/XwDlzmQFga5duIzkt9CcmlOwRoWsVBph6YP8
Qzz2sjHslzoLo0Nmc+HVZLE+uyKjeI5UFfPRu5c+rZgbwR27fDJY/O/JsFbV8wZm
5u504TkCgYEA849asWpIiWWg4U00txtM1etNe450x9bo6LgQ2QtcJTk5OPGuxUt3
sX51upYHat8VbEuEiEe70QEUySwqOiK6ofLn8zr8tPmg0smy0zt1DJy/tPHQ4ubX
A9DKMqYxHJPS4iRi5R7sOIjNFBfA2iUPEesQRPXAIMkIyevv6LBfg9UCgYEA0yIB
bftBTJc1WDbaOU339GYm8rLjiDHyK9DAOC548m85Bm1b45EtMoB5e+0VY/C10eLy
A2A7t7CpA9kLZRVlx4voQ21bTjEnO470GN6SfmhcLZ65WzvbZvLHj+zGjkV8+Lot
Av02h6hnwfwQhvN9XsZPyNk4IdcXK7045DnGzWMCgYAcF5HHWtHo/w7STbxhzkVL
eythr+mqTxBoHyraTeQf6vy9o6qb2PuCPmrHzZwnaHmpFwC/Uz7HeY9zMKPiNrU+
Dq1QMaKKISy6g0cb9ASpIr892JJWSXfNWdyogOCzQh2VtcquUKXAU48L3T2CK7oU
P/+NZKb3YRihaZQvS4CIzQKBgQC6UsxILu/Vk6u0CdRTtgcYW/4LOOurichZ+oNo
ETsTWCxPC7uH/NqSMucDAptZ81fBvjIt4INS/Ehr6OMxdcy4aTO0LZHiU2Z4HRQ1
zlYh0B9o8yZI6W4aUC7lSOOBMrmzFzoZ5TR2S5wliTlcnw0I0qIecfQjiRods4O9
hW94WQKBgHMxir/eMLR8z9hj1jpJh+eN3Uo6UM+5dzgqNUn4CJC2Tt6tIZPoxdhX
MimMH9yrRcCe068VlvqN+AsTTpkpceJT52dV5iXuEm1DoYgvhvD19MMlW/QmOY8w
5GHXzNf9wt2HkMZZcpCasMTfWW3LvtEomcA3USQbZt5NgX7QjAVo
-----END RSA PRIVATE KEY-----"""

private_key = private_key_part_1 + private_key_part_2

def comment_on_pr(content, title):
print("content:", content)
if content is None:
raise Exception("Content is required")

app_auth = AppAuth(app_id, private_key)
g = Github(auth=app_auth.get_installation_auth(installation_id))

# Get the pull request number and repository details
repo_name = os.environ['GITHUB_REPOSITORY']
print("repo_name:", repo_name)

# Read the event payload
with open(os.environ['GITHUB_EVENT_PATH'], 'r') as f:
event_payload = json.load(f)

# Extract the pull request number
pull_request_number = event_payload['pull_request']['number']
print("pull_request_number:", pull_request_number)

repo = g.get_repo(repo_name)
pull_request = repo.get_pull(int(pull_request_number))

# Get all comments on the pull request
comments = pull_request.get_issue_comments()

# Check if a comment by raftkeepeer-robot[bot] exists
comment_found = False
for comment in comments:
print("find comment:", comment)
if comment.user.login == "raftkeepeer-robot[bot]" and title in comment.body:
# Update the existing comment
comment.edit(content)
comment_found = True
break

# If not, create a new comment
if not comment_found:
pull_request.create_issue_comment(content)


def get_artifact_url(artifact_name):
# Authenticate with GitHub
app_auth = AppAuth(app_id, private_key)
g = Github(auth=app_auth.get_installation_auth(installation_id))

# Get the repository
repo_name = os.environ['GITHUB_REPOSITORY']
repo = g.get_repo(repo_name)

# Get the specific workflow run
run_id = os.environ['GITHUB_RUN_ID']
print("run_id:", run_id)
workflow_run = repo.get_workflow_run(int(run_id))

# List artifacts for the workflow run
artifacts = workflow_run.get_artifacts()

# Assuming you want the first artifact, get its download URL
for artifact in artifacts:
if artifact.name == artifact_name:
return artifact.archive_download_url

return None
34 changes: 34 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Test

on: # yamllint disable-line rule:truthy
workflow_call:
inputs:
sanitize:
required: true
type: string

jobs:
run:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Download binary
uses: actions/download-artifact@v4
with:
name: raftkeeper-binary-${{ inputs.sanitize }}
path: build/programs/

- name: Add executable privileges
run: sudo chmod 755 build/programs/raftkeeper

- name: Run integration Tests
run: bash .github/workflows/run-integration-test.sh tests/integration --junitxml=integration-test-report-${{ inputs.sanitize }}.xml

- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: integration-test-report-${{ inputs.sanitize }}.xml
path: tests/integration/integration-test-report-${{ inputs.sanitize }}.xml
26 changes: 26 additions & 0 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Build and Test macOS

on: # yamllint disable-line rule:truthy
workflow_call:

jobs:
run:
runs-on: macos-13
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Install tools
run: brew install ninja ccache cmake llvm@17

- name: Generate Makefile
run: export CC=$(brew --prefix llvm@17)/bin/clang CXX=$(brew --prefix llvm@17)/bin/clang++ && cmake -G Ninja -B ./build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}

- name: Build
working-directory: ${{github.workspace}}/build
run: ninja -j 10

- name: Run unit tests
working-directory: ${{github.workspace}}/build
run: ./src/rk_unit_tests --gtest_color=yes
Loading

0 comments on commit 33c6e89

Please sign in to comment.