96 lines
3.1 KiB
Python
96 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Compile the exact odict_or as C89 and exercise its operation ordering."""
|
|
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
source = Path(sys.argv[1]).read_text()
|
|
match = re.search(
|
|
r"static PyObject \*\nodict_or\(PyObject \*left, PyObject \*right\)\n\{.*?\n\}",
|
|
source, re.S,
|
|
)
|
|
assert match, "odict_or definition not found"
|
|
harness = r'''
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
typedef struct { int ordered; int dict; } PyObject;
|
|
typedef PyObject PyTypeObject;
|
|
static PyObject left_obj, right_obj, result_obj, not_implemented;
|
|
static PyObject *expected_type;
|
|
static int stage, fail_call, fail_update;
|
|
static int PyODict_Check(PyObject *obj) {
|
|
assert(stage == 0 && obj == &left_obj);
|
|
stage = 1;
|
|
return obj->ordered;
|
|
}
|
|
static PyTypeObject *Py_TYPE(PyObject *obj) {
|
|
assert(stage == 1 && obj == expected_type);
|
|
stage = 2;
|
|
return obj;
|
|
}
|
|
static int PyDict_Check(PyObject *obj) {
|
|
assert(stage == 2);
|
|
assert(obj == (left_obj.ordered ? &right_obj : &left_obj));
|
|
stage = 3;
|
|
return obj->dict;
|
|
}
|
|
#define Py_RETURN_NOTIMPLEMENTED return ¬_implemented
|
|
static PyObject *PyObject_CallOneArg(PyObject *type, PyObject *arg) {
|
|
assert(stage == 3 && type == expected_type && arg == &left_obj);
|
|
stage = 4;
|
|
return fail_call ? NULL : &result_obj;
|
|
}
|
|
static int mutablemapping_update_arg(PyObject *obj, PyObject *arg) {
|
|
assert(stage == 4 && obj == &result_obj && arg == &right_obj);
|
|
stage = 5;
|
|
return fail_update ? -1 : 0;
|
|
}
|
|
static void Py_DECREF(PyObject *obj) {
|
|
assert(stage == 5 && fail_update && obj == &result_obj);
|
|
stage = 6;
|
|
}
|
|
'''
|
|
harness += match.group(0)
|
|
harness += r'''
|
|
int main(void) {
|
|
int ordered, scenario;
|
|
PyObject *result;
|
|
for (ordered = 0; ordered < 2; ++ordered) {
|
|
for (scenario = 0; scenario < 4; ++scenario) {
|
|
left_obj.ordered = ordered;
|
|
right_obj.ordered = 1;
|
|
left_obj.dict = right_obj.dict = scenario != 0;
|
|
expected_type = ordered ? &left_obj : &right_obj;
|
|
fail_call = scenario == 1;
|
|
fail_update = scenario == 2;
|
|
stage = 0;
|
|
result = odict_or(&left_obj, &right_obj);
|
|
if (scenario == 0) {
|
|
assert(result == ¬_implemented && stage == 3);
|
|
} else if (scenario == 1) {
|
|
assert(result == NULL && stage == 4);
|
|
} else if (scenario == 2) {
|
|
assert(result == NULL && stage == 6);
|
|
} else {
|
|
assert(result == &result_obj && stage == 5);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory(prefix="vc6-odict-or-") as directory:
|
|
path = Path(directory)
|
|
(path / "test.c").write_text(harness)
|
|
compiled = subprocess.run([
|
|
"cc", "-std=c89", "-pedantic-errors", "-Wall", "-Wextra", "-Werror",
|
|
str(path / "test.c"), "-o", str(path / "test"),
|
|
])
|
|
if compiled.returncode:
|
|
sys.exit(compiled.returncode)
|
|
subprocess.run([str(path / "test")], check=True)
|
|
print("odict_or C89 compile and operand/rejection/construction/update/cleanup order: PASS (8 cases)")
|