Files
kaptan/ground_suite/windows/runner/test_utf8_from_utf16.py
T
Onur Özkan 4953f0a974 fix(ground-suite): handle failed UTF-16 conversion sizes
Reject failed UTF-16 sizing queries before allocating the UTF-8 result
and preserve terminating-NUL accounting.

Validation: nine host-stub cases with 16-bit wchar_t; native Windows
execution remains unverified.
2026-11-07 18:12:11 +03:00

122 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Run the production conversion function with a controlled Windows API stub.
This host check compiles only Utf8FromUtf16 from utils.cpp. It checks API arguments,
counts, and failure handling using 16-bit wchar_t and known UTF-16/UTF-8 data. It
does not build the Windows runner or validate Windows' Unicode implementation.
"""
import argparse
from pathlib import Path
import subprocess
PREFIX = r"""
#include <cassert>
#include <cstring>
#include <string>
static_assert(sizeof(wchar_t) == 2, "test requires UTF-16 code units");
constexpr unsigned CP_UTF8 = 65001;
constexpr unsigned WC_ERR_INVALID_CHARS = 0x80;
static const wchar_t* expected_source;
static std::string expected_utf8;
static int query_result;
static int conversion_result;
static int calls;
int WideCharToMultiByte(unsigned code_page, unsigned flags, const wchar_t* source,
int input_length, char* target, int capacity,
const char* default_char, int* used_default_char) {
++calls;
assert(code_page == CP_UTF8 && flags == WC_ERR_INVALID_CHARS);
assert(source == expected_source && input_length == -1);
assert(default_char == nullptr && used_default_char == nullptr);
if (target == nullptr) {
assert(calls == 1 && capacity == 0);
return query_result;
}
assert(calls == 2 && capacity == query_result);
assert(capacity == static_cast<int>(expected_utf8.size()) + 1);
std::memcpy(target, expected_utf8.c_str(), expected_utf8.size() + 1);
return conversion_result;
}
"""
SUFFIX = r"""
void configure(const wchar_t* source, const std::string& expected, int query,
int converted) {
expected_source = source;
expected_utf8 = expected;
query_result = query;
conversion_result = converted;
calls = 0;
}
int main() {
assert(Utf8FromUtf16(nullptr).empty());
assert(calls == 0);
const wchar_t empty[] = {0};
configure(empty, "", 1, 1);
assert(Utf8FromUtf16(empty).empty());
assert(calls == 2);
// Independent known encoding: A, U+00E9, U+1F642 (a surrogate pair).
const wchar_t text[] = {0x0041, 0x00e9, 0xd83d, 0xde42, 0};
const std::string utf8 = "A\xc3\xa9\xf0\x9f\x99\x82";
configure(text, utf8, 8, 8);
assert(Utf8FromUtf16(text) == utf8);
assert(calls == 2);
// WC_ERR_INVALID_CHARS reports an isolated surrogate as a zero query result.
const wchar_t invalid[] = {0xd800, 0};
for (int failed : {0, -1}) {
configure(invalid, "", failed, 0);
assert(Utf8FromUtf16(invalid).empty());
assert(calls == 1);
}
for (int failed_or_mismatched : {0, -1, 7, 9}) {
configure(text, utf8, 8, failed_or_mismatched);
assert(Utf8FromUtf16(text).empty());
assert(calls == 2);
}
}
"""
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--work-dir", type=Path, required=True)
parser.add_argument("--cxx", default="c++")
args = parser.parse_args()
work = args.work_dir.resolve()
work.mkdir(parents=True, exist_ok=True)
source = Path(__file__).with_name("utils.cpp").read_text()
start = source.index("std::string Utf8FromUtf16(")
end = source.index("\n}", start) + 2
fixture = work / "utf8_from_utf16.cpp"
fixture.write_text(PREFIX + source[start:end] + SUFFIX)
binary = work / "utf8_from_utf16_test"
subprocess.run(
[
args.cxx,
"-std=c++17",
"-fshort-wchar",
"-Wall",
"-Wextra",
"-Werror",
"-pedantic",
str(fixture),
"-o",
str(binary),
],
check=True,
)
subprocess.run([str(binary)], check=True)
print("Utf8FromUtf16 host stub checks passed (9 cases, 16-bit wchar_t)")
if __name__ == "__main__":
main()