mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-14 12:10:30 +00:00
8f92417a2f
- Add a new ClientJavaExec Gradle task, which is used for client-side tests. This: - Copies the exec spec from another JavaExec task. - Sets some additional system properties to configure on gametest framework. - Runs Java inside an X framebuffer (when available), meaning we don't need to spin up a new window. We also configure this task so that only one instance can run at once, meaning we don't spawn multiple MC windows at once! - Port our 1.16 client test framework to 1.19. This is mostly the same as before, but screenshots no longer do a golden test: they /just/ write to a folder. Screenshots are compared manually afterwards. This is still pretty brittle, and there's a lot of sleeps scattered around in the code. It's not clear how well this will play on CI. - Roll our own game test loader, rather than relying on the mod loader to do it for us. This ensures that loading is consistent between platforms (we already had to do some hacks for Forge) and makes it easier to provide custom logic for loading client-only tests. - Run several client tests (namely those involving monitor rendering) against Sodium and Iris too. There's some nastiness here to set up new Loom run configurations and automatically configure Iris to use Complementary Shaders, but it's not too bad. These tests /don't/ run on CI, so it doesn't need to be as reliable.
149 lines
4.3 KiB
Python
Executable File
149 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Parse reports generated by Gradle and convert them into GitHub annotations.
|
|
|
|
See https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md and
|
|
https://github.com/actions/toolkit/blob/master/docs/commands.md.
|
|
"""
|
|
|
|
import os.path
|
|
import pathlib
|
|
import re
|
|
import xml.etree.ElementTree as ET
|
|
from typing import Optional, Tuple
|
|
|
|
LUA_ERROR_LOCATION = re.compile(r"^\s+(/[\w./-]+):(\d+):", re.MULTILINE)
|
|
JAVA_LUA_ERROR_LOCATION = re.compile(r"^java.lang.IllegalStateException: (/[\w./-]+):(\d+):")
|
|
JAVA_ERROR_LOCATION = re.compile(r"^\tat ([\w.]+)\.[\w]+\([\w.]+:(\d+)\)$", re.MULTILINE)
|
|
ERROR_MESSAGE = re.compile(r"(.*)\nstack traceback:", re.DOTALL)
|
|
|
|
SPACES = re.compile(r"\s+")
|
|
|
|
SOURCE_LOCATIONS = [
|
|
"src/main/java",
|
|
"src/main/resources/data/computercraft/lua",
|
|
"src/test/java",
|
|
"src/test/resources",
|
|
]
|
|
|
|
PROJECT_LOCATIONS = [
|
|
"projects/core-api",
|
|
"projects/core",
|
|
"projects/common-api",
|
|
"projects/common",
|
|
"projects/fabric-api",
|
|
"projects/fabric",
|
|
"projects/forge-api",
|
|
"projects/forge",
|
|
]
|
|
|
|
TEST_REPORTS = []
|
|
|
|
|
|
def find_file(path: str) -> Optional[str]:
|
|
while len(path) > 0 and path[0] == "/":
|
|
path = path[1:]
|
|
|
|
for project in PROJECT_LOCATIONS:
|
|
for source_dir in SOURCE_LOCATIONS:
|
|
child_path = os.path.join(project, source_dir, path)
|
|
if os.path.exists(child_path):
|
|
return child_path
|
|
|
|
return None
|
|
|
|
|
|
def find_location(message: str) -> Optional[Tuple[str, str]]:
|
|
location = LUA_ERROR_LOCATION.search(message)
|
|
if location:
|
|
file = find_file(location[1])
|
|
if file:
|
|
return file, location[2]
|
|
|
|
location = JAVA_LUA_ERROR_LOCATION.search(message)
|
|
if location:
|
|
file = find_file(location[1])
|
|
if file:
|
|
return file, location[2]
|
|
|
|
for location in JAVA_ERROR_LOCATION.findall(message):
|
|
file = find_file(location[0].replace(".", "/") + ".java")
|
|
if file:
|
|
return file, location[1]
|
|
|
|
return None
|
|
|
|
|
|
def _parse_junit_file(path: pathlib.Path):
|
|
for testcase in ET.parse(path).findall(".//testcase"):
|
|
for result in testcase:
|
|
if result.tag == "skipped":
|
|
continue
|
|
|
|
name = f'{testcase.attrib["classname"]}.{testcase.attrib["name"]}'
|
|
message = result.attrib.get("message")
|
|
full_message = result.text or message
|
|
|
|
location = find_location(full_message)
|
|
error = ERROR_MESSAGE.match(message)
|
|
if error:
|
|
error = error[1]
|
|
else:
|
|
error = message
|
|
|
|
if location:
|
|
print(f'## {location[0]}:{location[1]}: {name} failed: {SPACES.sub(" ", error)}')
|
|
else:
|
|
print(f"::error::{name} failed")
|
|
|
|
print("::group::Full error message")
|
|
print(full_message)
|
|
print("::endgroup")
|
|
|
|
|
|
def parse_junit() -> None:
|
|
"""
|
|
Scrape JUnit test reports for errors. We determine the location from the Lua
|
|
or Java stacktrace.
|
|
"""
|
|
print("::add-matcher::.github/matchers/junit.json")
|
|
|
|
for project in PROJECT_LOCATIONS:
|
|
for path in pathlib.Path(os.path.join(project, "build/test-results/test")).glob("TEST-*.xml"):
|
|
_parse_junit_file(path)
|
|
|
|
for path in pathlib.Path(os.path.join(project, "build/test-results")).glob("run*.xml"):
|
|
_parse_junit_file(path)
|
|
|
|
print("::remove-matcher owner=junit::")
|
|
|
|
|
|
def _parse_checkstyle(path: pathlib.Path):
|
|
for file in ET.parse(path).getroot():
|
|
for error in file:
|
|
filename = os.path.relpath(file.attrib["name"])
|
|
|
|
attrib = error.attrib
|
|
print(
|
|
f'{attrib["severity"]} {filename}:{attrib["line"]}:{attrib.get("column", 1)}: {SPACES.sub(" ", attrib["message"])}'
|
|
)
|
|
|
|
|
|
def parse_checkstyle() -> None:
|
|
"""
|
|
Scrape JUnit test reports for errors. We determine the location from the Lua
|
|
or Java stacktrace.
|
|
"""
|
|
print("::add-matcher::.github/matchers/checkstyle.json")
|
|
|
|
for project in PROJECT_LOCATIONS:
|
|
for path in pathlib.Path(os.path.join(project, "build/reports/checkstyle/")).glob("*.xml"):
|
|
_parse_checkstyle(path)
|
|
|
|
print("::remove-matcher owner=checkstyle::")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parse_junit()
|
|
parse_checkstyle()
|