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.
122 lines
3.7 KiB
Python
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()
|