Files
EVEngine/scripts/tests/test_raw_string_literal.py
西风逍遥游 811eff1c3a ci(quality): warnings-as-errors gate, sanitizers, smoke, Python lint/tests
- EVENGINE_ENABLE_STRICT_WARNINGS option: MSVC /W4 /WX, GCC/Clang
  -Wall -Wextra -Werror, with documented noise suppressions and
  -isystem/-external:I for third-party headers; enabled on the
  Windows/Linux debug CI jobs.
- Fix warnings across engine, modules and tests exposed by the gate,
  including real bugs (incomplete-type deletes, Timespan microsecond
  overflow, overload hiding, enum switch gaps, dead code).
- Add Linux ASan+UBSan job, example smoke step, python-tools test
  job, ruff config (scripts+tools), weekly Linux coverage job and a
  CodeQL workflow.
2026-08-22 02:27:37 +08:00

87 lines
3.4 KiB
Python

"""Regression tests for the embedded-script raw-string literal generator.
The engine embeds src/scripts/*.nut and *.glsl into a generated C++ file as
raw-string literals. A fixed R"(...)" delimiter would break the build the day
a script contained the terminator sequence )"; the generator (see
src/scripts/raw_string_literal.cmake) picks a per-content delimiter that is
guaranteed absent, so embedding is always safe.
"""
import pathlib
import re
import subprocess
import tempfile
import unittest
ROOT = pathlib.Path(__file__).resolve().parents[2]
RAW_LITERAL = ROOT / "src" / "scripts" / "raw_string_literal.cmake"
def run_cmake(harness: pathlib.Path, out: pathlib.Path) -> None:
subprocess.run(
[
"cmake",
f"-DOUT={out.as_posix()}",
f"-DRAW_LITERAL={RAW_LITERAL.as_posix()}",
"-P",
harness.as_posix(),
],
check=True,
capture_output=True,
text=True,
)
def roundtrip(literal: str) -> str:
"""Extract the content embedded in a C++ raw-string literal."""
m = re.match(r'^R"([A-Za-z_][A-Za-z0-9_]*)\((.*)\)\1"$', literal, re.S)
assert m, f"literal is not a valid raw string: {literal!r}"
delim, content = m.group(1), m.group(2)
assert f"){delim}\"" not in content, "terminator leaked into content"
return content
class RawStringLiteralTest(unittest.TestCase):
def test_delimiter_selection_and_roundtrip(self):
cases = {
# Plain script: default delimiter.
"plain": 'print("hi")\nlocal x = 1; // comment\n',
# The old landmine: )" inside the source.
"terminator": 'ui.button("follow (1)", "m_follow");\n',
# Both the default and a grown delimiter present.
"grown": 'a = ")eve"\nb = ")"\nc = "x)evexx"\n',
# Everything hostile at once.
"hostile": 's = "a\\b\\\"c"\n@dollar@ ${braces} )evex" )"\n//\u4e2d\u6587\n',
# Empty source (EVENGINE_BUILD_DEMO=OFF embeds an empty demo).
"empty": "",
}
with tempfile.TemporaryDirectory() as tmp:
tmp = pathlib.Path(tmp)
harness = tmp / "harness.cmake"
out = tmp / "out.txt"
lines = ['include("${RAW_LITERAL}")', 'set(_out "")']
for idx, (name, content) in enumerate(cases.items(), start=1):
# CMake does not interpret \n escapes in quoted strings, so
# inject multi-line content verbatim via a bracket argument.
lines.append(f"set(_content_{idx} [====[\n{content}]====])")
lines.append(
f"eve_raw_string_literal(_lit_{idx} \"${{_content_{idx}}}\")"
)
lines.append(f'string(APPEND _out "CASE{idx}|${{_lit_{idx}}}\\n")')
lines.append('file(WRITE "${OUT}" "${_out}")')
harness.write_text("\n".join(lines) + "\n", encoding="utf-8")
run_cmake(harness, out)
results = out.read_text(encoding="utf-8")
markers = list(re.finditer(r"CASE(\d+)\|", results))
for idx, (name, content) in enumerate(cases.items(), start=1):
m = markers[idx - 1]
end = markers[idx].start() if idx < len(markers) else len(results)
literal = results[m.end() : end].rstrip("\n")
self.assertEqual(roundtrip(literal), content, f"roundtrip failed: {name}")
if __name__ == "__main__":
unittest.main()