diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 368676a3..d105cd48 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,26 +32,19 @@ jobs: - name: Install test dependencies run: | - sudo add-apt-repository -y ppa:deadsnakes/ppa sudo apt-get -y install \ valgrind \ - gdb \ - python${{ matrix.python-version }} \ - python3.10{,-full} - - # Using these actions lead to Python binaries that cause SIGSEGV during - # C unit tests. See - # https://github.com/actions/setup-python/issues/442. - - # - name: Install Python - # uses: actions/setup-python@v4 - # with: - # python-version: ${{ matrix.python-version }} - - # - name: Install Python 3.10 - # uses: actions/setup-python@v4 - # with: - # python-version: "3.10" + gdb + + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }}-dev + + - name: Install Python 3.10 + uses: actions/setup-python@v4 + with: + python-version: "3.10" - name: Compile Austin run: | diff --git a/test/cunit/__init__.py b/test/cunit/__init__.py index 5ebe70a3..9458b48f 100644 --- a/test/cunit/__init__.py +++ b/test/cunit/__init__.py @@ -1,10 +1,10 @@ import ctypes import re -from ctypes import CDLL, POINTER, Structure, c_char_p, cast +from ctypes import CDLL, POINTER, Structure, c_char_p, c_long, c_void_p, cast from pathlib import Path from subprocess import PIPE, STDOUT, run from types import ModuleType -from typing import Any, Callable, Optional, Type +from typing import Any, Callable, Optional, Type, Union from pycparser import c_ast, c_parser from pycparser.plyparser import ParseError @@ -90,7 +90,9 @@ def compile( class CFunctionDef: - def __init__(self, name: str, args: list[str], rtype: Any) -> None: + def __init__( + self, name: str, args: list[tuple[str, Type[ctypes._SimpleCData]]], rtype: Any + ) -> None: self.name = name self.args = args self.rtype = rtype @@ -101,7 +103,7 @@ def __init__(self, name: str, fields: list[str]) -> None: self.name = name self.fields = fields self.methods = [] - self.constructor = False + self.constructor = None class CType(Structure): @@ -113,16 +115,20 @@ def __del__(self) -> None: self.destroy() def __repr__(self) -> str: - return f"<{self.name} CObject at {self.__cself__}>" + return f"<{self.__class__.__name__} CObject at {self.__cself__}>" class CFunction: def __init__(self, cfuncdef: CFunctionDef, cfunc: Callable[..., Any]) -> None: self.__name__ = cfuncdef.name - self.__args__ = cfuncdef.args + self.__args__ = [_[0] for _ in cfuncdef.args] self.__cfunc__ = cfunc - if cfuncdef.rtype is not None: - self.__cfunc__.restype = cfuncdef.rtype + + # Prevent argument values from being truncated/mangled + self.__cfunc__.argtypes = [_[1] for _ in cfuncdef.args] + self.__cfunc__.restype = ( + cfuncdef.rtype if cfuncdef.rtype is not None else c_long + ) self._posonly = all(_ is None for _ in self.__args__) @@ -207,7 +213,6 @@ def __init__(self) -> None: self.functions = [] def _get_type(self, node: c_ast.Node) -> None: - print(node) return self.types[" ".join(node.type.type.names)] def visit_Typedef(self, node: c_ast.Node) -> None: @@ -231,8 +236,16 @@ def visit_Decl(self, node: c_ast.Node) -> None: if isinstance(ret_type, c_ast.PtrDecl): if "".join(ret_type.type.type.names) == "char": rtype = c_char_p + else: + rtype = c_void_p + args = ( - [_.name if hasattr(_, "name") else None for _ in node.type.args.params] + [ + (_.name, c_void_p if isinstance(_.type, c_ast.PtrDecl) else c_long) + if hasattr(_, "name") + else None + for _ in node.type.args.params + ] if node.type.args is not None else [] ) @@ -271,7 +284,7 @@ def collect(self, decl: str) -> dict[str, CTypeDef]: return { k: v for k, v in self.types.items() - if isinstance(v, CTypeDef) and v.constructor + if isinstance(v, CTypeDef) and v.constructor is not None } diff --git a/test/cunit/test_cache.py b/test/cunit/test_cache.py index ec809bd1..84a41844 100644 --- a/test/cunit/test_cache.py +++ b/test/cunit/test_cache.py @@ -1,9 +1,12 @@ +from ctypes import c_void_p from test.cunit import C from test.cunit.cache import Chain, HashTable, LruCache, Queue, QueueItem import pytest NULL = 0 +C.free.argtypes = [c_void_p] +C.malloc.restype = c_void_p def test_queue_item(): @@ -31,7 +34,7 @@ def test_queue(qsize): assert qsize == 0 or not q.is_empty() assert q.is_full() - assert q.enqueue(42, 42) is NULL + assert q.enqueue(42, 42) is None assert values == [q.dequeue() for _ in range(qsize)] diff --git a/test/utils.py b/test/utils.py index f659ff28..2b89f035 100644 --- a/test/utils.py +++ b/test/utils.py @@ -27,6 +27,7 @@ from collections import Counter, defaultdict from io import BytesIO, StringIO from pathlib import Path +from shutil import rmtree from subprocess import PIPE, CompletedProcess, Popen, check_output, run from test import PYTHON_VERSIONS from time import sleep @@ -109,7 +110,7 @@ def bt(binary: Path) -> str: result = gdb(["bt full", "q"], str(binary), target_dir / "CoreDump") crash.unlink() - target_dir.rmdir() + rmtree(str(target_dir)) return result