Skip to content

Commit

Permalink
testsuite: work on temp data scoping
Browse files Browse the repository at this point in the history
  • Loading branch information
mara004 committed Dec 15, 2023
1 parent 5b3941b commit 7170292
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 59 deletions.
56 changes: 36 additions & 20 deletions tests/ctypesgentest.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import os
import sys
import glob
import json
import atexit
import types
import subprocess
import shutil
from itertools import product
import tempfile
from pathlib import Path

import ctypesgen.__main__
Expand All @@ -15,6 +14,22 @@

TEST_DIR = Path(__file__).resolve().parent
COMMON_DIR = TEST_DIR/"common"
TMP_DIR = TEST_DIR/"tmp"
COUNTER = 0
CLEANUP_OK = bool(int(os.environ.get("CLEANUP_OK", "1")))


def init_tmpdir():
if TMP_DIR.exists():
shutil.rmtree(TMP_DIR)
TMP_DIR.mkdir()

def cleanup_tmpdir():
if CLEANUP_OK:
shutil.rmtree(TMP_DIR)

init_tmpdir()
atexit.register(cleanup_tmpdir)


def ctypesgen_main(args):
Expand All @@ -32,14 +47,22 @@ def module_from_code(name, python_code):

def generate(header_str, args=[], lang="py"):

tmp_in = TEST_DIR/"tmp_in.h"
tmp_in.write_text(header_str)
tmp_out = TEST_DIR/"tmp_out.py"
ctypesgen_main(["-i", tmp_in, "-o", tmp_out, "--output-language", lang, *args])
content = tmp_out.read_text()
# use custom tempfiles scoping so we may retain data for inspection
# also note that python stdlib tempfiles don't play well with windows

global COUNTER
COUNTER += 1

tmp_in.unlink()
tmp_out.unlink()
tmp_in = TMP_DIR/f"in_header_{COUNTER:02d}.h"
tmp_in.write_text(header_str)
try:
tmp_out = TMP_DIR/f"out_bindings_{COUNTER:02d}.py"
ctypesgen_main(["-i", tmp_in, "-o", tmp_out, "--output-language", lang, *args])
content = tmp_out.read_text()
finally:
if CLEANUP_OK:
tmp_in.unlink()
tmp_out.unlink()

if lang.startswith("py"):
return module_from_code("tmp_module", content)
Expand All @@ -49,12 +72,6 @@ def generate(header_str, args=[], lang="py"):
assert False


def cleanup(filepattern="temp.*"):
fnames = glob.glob(filepattern)
for fname in fnames:
os.unlink(fname)


def set_logging_level(log_level):
messages.log.setLevel(log_level)

Expand Down Expand Up @@ -168,11 +185,9 @@ def _create_common_files():
void bar(struct mystruct *m) { }
"""

try:
COMMON_DIR.mkdir()
except FileExistsError:
if COMMON_DIR.exists():
shutil.rmtree(COMMON_DIR)
COMMON_DIR.mkdir()
COMMON_DIR.mkdir()

for (name, source) in names.items():
with (COMMON_DIR/name).open("w") as f:
Expand Down Expand Up @@ -202,4 +217,5 @@ def cleanup_common():
# Attention: currently not working on MS Windows.
# cleanup_common() tries to delete "common.dll" while it is still loaded
# by ctypes. See unittest for further details.
shutil.rmtree(COMMON_DIR)
if CLEANUP_OK:
shutil.rmtree(COMMON_DIR)
67 changes: 28 additions & 39 deletions tests/testsuite.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""Simple test suite using unittest.
By clach04 (Chris Clark).
Originally written by clach04 (Chris Clark).
Calling:
Expand All @@ -21,6 +21,9 @@
Aims to test for regressions. Where possible use stdlib to
avoid the need to compile C code.
Note, you may set CLEANUP_OK=0 to retain generated data.
This can be useful for inspection.
"""

import sys
Expand All @@ -29,19 +32,18 @@
import json as JSON
import math
import unittest
import logging
from subprocess import Popen, PIPE
from tempfile import NamedTemporaryFile

from tests.ctypesgentest import (
cleanup,
cleanup_common,
ctypesgen_version,
generate,
generate_common,
JsonHelper,
set_logging_level,
TEST_DIR,
TMP_DIR,
COUNTER,
CLEANUP_OK,
)


Expand Down Expand Up @@ -115,7 +117,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del cls.module
cleanup()

def test_getenv_returns_string(self):
""" Test string return """
Expand Down Expand Up @@ -166,17 +167,23 @@ def test_getenv_returns_null(self):
# means the dll is loaded to memory.
# On Linux/macOS this is no problem, as .so libaries can be overwritten/deleted
# on file system while still loaded to memory.
@unittest.skipIf(
sys.platform == "win32",
"Currently not working on Windows. See code comment for details."
)

# NOTE skip commented out for testing
# @unittest.skipIf(
# sys.platform == "win32",
# "Currently not working on Windows. See code comment for details."
# )
class CommonHeaderTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
generate_common()

@classmethod
def tearDownClass(cls):
# try to fix the issue described above
if sys.platform.startswith("win32"):
from .common._ctg_loader import _libs as loader_libs
ctypes.windll.kernel32.FreeLibrary(loader_libs["c"]._handle)
cleanup_common()

# NOTE `common` is a meta-module hosted by the test class, and {a,b}{shared,unshared} are the actual python files in question
Expand Down Expand Up @@ -227,7 +234,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del cls.module
cleanup()

def test_stdbool_type(self):
"""Test if bool is parsed correctly"""
Expand Down Expand Up @@ -261,7 +267,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del cls.module
cleanup()

def test_int_types(self):
"""Test if different integer types are parsed correctly"""
Expand Down Expand Up @@ -316,7 +321,6 @@ def _json(self, name):
@classmethod
def tearDownClass(cls):
del cls.module, cls.json
cleanup()

def test_macro_constant_int(self):
"""Tests from simple_macros.py"""
Expand Down Expand Up @@ -638,7 +642,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del StructuresTest.module
cleanup()

def test_struct_json(self):
json_ans = [
Expand Down Expand Up @@ -2053,7 +2056,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del cls.module
cleanup()

def test_sin(self):
"""Based on math_functions.py"""
Expand Down Expand Up @@ -2103,7 +2105,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del cls.module, cls.json
cleanup()

def test_enum(self):
self.assertEqual(EnumTest.module.TEST_1, 0)
Expand Down Expand Up @@ -2215,7 +2216,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del cls.json
cleanup()

def test_function_prototypes_json(self):
json_ans = [
Expand Down Expand Up @@ -2368,7 +2368,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del cls.module
cleanup()

def test_longdouble_type(self):
"""Test if long double is parsed correctly"""
Expand Down Expand Up @@ -2442,7 +2441,6 @@ def test_unchecked_prototype(self):
@classmethod
def tearDownClass(cls):
del cls.module
cleanup()


class ConstantsTest(unittest.TestCase):
Expand Down Expand Up @@ -2482,7 +2480,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del ConstantsTest.module
cleanup()

def test_integer_constants(self):
"""Test if integer constants are parsed correctly"""
Expand Down Expand Up @@ -2534,7 +2531,6 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
del NULLTest.module
cleanup()

def test_null_type(self):
"""Test if NULL is parsed correctly"""
Expand Down Expand Up @@ -2572,7 +2568,6 @@ def setUpClass(cls):
def tearDownClass(cls):
del cls.module
os.remove(cls.mac_roman_file)
cleanup()

def test_macroman_encoding_source(self):
module = MacromanEncodeTest.module
Expand All @@ -2590,23 +2585,17 @@ def setUpClass(cls):

def test_type_error_catch(self):
with self.assertRaises(ctypes.ArgumentError):
self.module.printf(123)
# in case this slipped through as binary data, you would see chr(33) = '!' at the end
self.module.printf(33)

def test_call(self):
tmp = TEST_DIR/"tmp_testdata.txt"
tmp = TMP_DIR/f"out_data_{COUNTER:02d}.txt"
tmp.touch()
c_file = self.module.fopen(str(tmp).encode(), b"w")
self.module.fprintf(c_file, b"Test variadic function: %s %d", b"Hello", 123)
self.module.fclose(c_file)
assert tmp.read_bytes() == b"Test variadic function: Hello 123"
tmp.unlink()


def main():
set_logging_level(logging.CRITICAL) # do not log anything
unittest.main()
return 0


if __name__ == "__main__":
sys.exit(main())
try:
c_file = self.module.fopen(str(tmp).encode(), b"w")
self.module.fprintf(c_file, b"Test variadic function: %s %d", b"Hello", 123)
self.module.fclose(c_file)
assert tmp.read_bytes() == b"Test variadic function: Hello 123"
finally:
if CLEANUP_OK:
tmp.unlink()

0 comments on commit 7170292

Please sign in to comment.