コード例 #1
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_default_link():
    # 'm' should always be linked to on Linux.
    self = CSlug(RESOURCES / "basic.c")
    if platform.system() == "Linux":
        assert "-lm" in self.compile_command()[0]

    # A library that is added both explicitly add by default should not be
    # duplicated.
    self = CSlug(RESOURCES / "basic.c", links="m")
    assert self.compile_command()[0].count("-lm") == 1
コード例 #2
0
def test_unicode_identifiers(source):
    """Test unicode function/variable names."""
    delayed_skip_if_unsupported("Unicode identifiers",
                                gcc=(10, ),
                                tcc=False,
                                pcc=False,
                                clang=(3, 3),
                                pgcc=False)

    slug = CSlug(anchor(name()), source)
    slug.make()
    assert slug.dll.㟐(5) == 4
コード例 #3
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_bit_ness():
    """Check 32/64b-bits behaves as expected by looking at integer overflow.
    """
    from cslug._cslug import BIT_NESS

    self = CSlug(*anchor(name(), io.StringIO("""

        # include <stddef.h>
        # include <stdbool.h>

        bool adding_1_causes_overflow(size_t x) {
            return (x + 1) < x;
        }

    """))) # yapf: disable

    for int_size in range(8, 128, 8):
        # Maximum possible value for an unsigned int of size ``int_size``.
        int_max = (1 << int_size) - 1
        if BIT_NESS <= int_size:
            assert adding_1_causes_overflow_py(int_max)
            assert self.dll.adding_1_causes_overflow(int_max)

        else:
            assert not adding_1_causes_overflow_py(int_max)
            assert not self.dll.adding_1_causes_overflow(int_max)
コード例 #4
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_custom_include():
    """``#include`` an awkwardly located header file using the ``-I`` option."""

    self = CSlug(anchor(name()),
                 io.StringIO("""\
        #include <header-in-random-location.h>

        int foo() { return REMOTE_CONSTANT; }

    """), flags=["-I", RESOURCES / "somewhere-remote"])  # yapf: disable

    assert self.dll.foo() == 13

    # Test without the custom include path. It should lead to a failed build.
    # This awkward construct asserts that an error is raised however pgcc
    # indeterminantly ignores such errors and produces non functional
    # exectaubles. In this case we should xfail().
    self.flags.clear()
    try:
        self.make()

        if cc_version()[0] == "pgcc":
            pytest.xfail("pgcc silently created an invalid executable.")
        else:
            assert 0, "No build error was raised"
    except exceptions.BuildError:
        return
コード例 #5
0
ファイル: test_structs.py プロジェクト: bwoodsend/cslug
def test_long_repr():
    slug = CSlug(
        anchor(name()),
        io.StringIO("""

    #include <stdint.h>

    typedef struct LongNamedThing{
        int64_t very_long_name_1; int64_t very_long_name_2;
        int64_t very_long_name_3; int64_t very_long_name_4;
    } LongNamedThing;

    """))

    thing = slug.dll.LongNamedThing(*(range(4)))

    assert repr(thing) == """
LongNamedThing(very_long_name_1=0, very_long_name_2=1,
               very_long_name_3=2, very_long_name_4=3)
""".lstrip()

    thing = slug.dll.LongNamedThing(*(1 << 30 << i for i in range(4)))

    assert repr(thing) == """
LongNamedThing(very_long_name_1=1073741824,
               very_long_name_2=2147483648,
               very_long_name_3=4294967296,
               very_long_name_4=8589934592)
""".lstrip()
コード例 #6
0
def test_non_escaped_unicode_literal(source):
    """Test a C source file may contain unicode characters in string literals.
    """
    delayed_skip_if_unsupported("Unicode literals", pgcc=False)
    self = CSlug(anchor(name()), source)
    self.make()
    assert self.dll.a() == "㟐"
コード例 #7
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_bytes():
    self = CSlug(anchor(name()), DEMOS / "bytes" / "encrypt.c")
    self.make()

    def _crypt(data, key, multiplier):
        out = ctypes.create_string_buffer(len(data))
        self.dll.encrypt(data, len(data), out, key, len(key), multiplier)
        return out[:]

    def encrypt(data, key):
        return _crypt(data, key, 1)

    def decrypt(data, key):
        return _crypt(data, key, -1)

    data = bytes(range(256))
    key = b"secret"
    encrypted = encrypt(data, key)
    assert isinstance(encrypted, bytes)

    # A pure Python equivalent of the C code we're testing.
    pure_py = bytes(
        (i + j) % 256 for (i, j) in zip(data, itertools.cycle(key)))
    assert encrypted == pure_py

    assert decrypt(encrypted, key) == data
    leaks(lambda: encrypt(data, key), n=100, tol=len(data) * 10)
コード例 #8
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_with_header():
    header_name = "header-" + name().stem + ".h"
    cake = random.randint(0, 256)
    header = Header(DUMP / header_name, defines={"cake": cake})

    source, = anchor(name().with_suffix(".c"))
    source.write_text("""\
    #include "%s"

    int get_cake() {
        return cake;
    }
    """ % header_name)

    self = CSlug(*anchor(name()), source, headers=header)  # yapf: disable

    try:
        old = os.getcwd()
        os.chdir(DUMP)
        self.make()
    finally:
        os.chdir(old)

    assert self.headers[0].path.exists()
    assert self.dll.get_cake() == cake
コード例 #9
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_basic(true_file):
    SOURCE = RESOURCES / "basic.c"
    if true_file:
        file = SOURCE
    else:
        file = io.StringIO(SOURCE.read_text())
    self = CSlug(*anchor(name(), file)) # yapf: disable

    assert not self.path.exists()
    assert not self.types_map.json_path.exists()
    self.dll
    assert self.path.exists()
    assert self.types_map.json_path.exists()

    assert hasattr(self.dll, "add_1")
    assert hasattr(self.dll, "times_2")

    assert self.dll.add_1.restype == ctypes.c_int
    assert self.dll.add_1.argtypes == [ctypes.c_int]
    assert self.dll.times_2.restype == ctypes.c_float
    assert self.dll.times_2.argtypes == [ctypes.c_float]

    assert isinstance(self.dll.add_1(3), int)
    assert self.dll.add_1(3) == 4
    assert isinstance(self.dll.times_2(6), float)
    assert self.dll.times_2(7.5) == 15
    assert self.dll.times_2(7) == 14.0

    with pytest.raises(Exception):
        self.dll.add_1()
    with pytest.raises(Exception):
        self.dll.add_1(2.0)
コード例 #10
0
def test_unicode_source_path():
    """Test the filename to a .c source file may contain unicode."""
    unicode_filename = DUMP / ("㟐㟐㟐" + name().stem + ".c")
    shutil.copy(RESOURCES / "basic.c", unicode_filename)

    self = CSlug(unicode_filename)
    assert self.sources[0].exists()
    self.make()
    assert self.dll.add_1(-3) == -2
コード例 #11
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_link():
    # TODO: This isn't much of a test. Try to think of a cross platform library
    #       to link against. Or a non cross platform library for each OS.
    self = CSlug(RESOURCES / "basic.c", links="c")
    if platform.system() == "Linux":
        assert self.links == ["c", "m"]
    else:
        assert self.links == ["c"]
    assert "-lc" in self.compile_command()[0]
コード例 #12
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_build_error():

    self = CSlug(*anchor(
        name(), io.StringIO("""
        int invalid() { syntax }
        """)))

    with pytest.raises(exceptions.BuildError):
        self.make()

    assert getattr(self, "_dll", None) is None
コード例 #13
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_propagate_build_warnings():

    self = CSlug(*anchor(name(), io.StringIO("""
        #warning "Not a good idea."
        void foo() {  }
    """))) # yapf: disable

    with pytest.warns(exceptions.BuildWarning, match="Not a good idea."):
        self.make()

    assert hasattr(self.dll, "foo")
コード例 #14
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_no_anchor():
    old_cwd = os.getcwd()
    try:
        os.chdir(DUMP)
        self = CSlug(name().name, io.StringIO(""))
        # The path eventually given to CDLL must not be just a name. i.e. It
        # can either be absolute, include a folder name e.g. `foo/bar` or
        # prefixed with `./`. In all these cases, `os.path.dirname()` will emit
        # a non-empty string.
        assert os.path.dirname(self.dll._name)

    finally:
        os.chdir(old_cwd)
コード例 #15
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_str():
    self = CSlug(anchor(name()), DEMOS / "strings" / "reverse.c")
    self.make()

    def reverse_test(text):
        out = ctypes.create_unicode_buffer(len(text) + 1)
        self.dll.reverse(text, out, len(text)) is None
        assert out.value == text[::-1]
        assert out[:] == text[::-1] + "\x00"

    reverse_test("hello")

    # A 10th of the memory that would be leaked if `reverse_test()` leaked.
    tolerance = ctypes.sizeof(ctypes.create_unicode_buffer("hello" * 100)) * 10
    leaks(lambda: reverse_test("hello" * 100), n=100, tol=tolerance)
コード例 #16
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_no_cc_or_blocked_error():

    self = CSlug(*anchor(name(), io.StringIO("")))

    old = os.environ.copy()
    try:
        misc.hide_from_PATH("gcc")
        misc.hide_from_PATH("clang")
        os.environ.pop("CC", None)
        with pytest.raises(exceptions.NoGccError):
            self.make()
    finally:
        os.environ.clear()
        os.environ.update(old)

    with pytest.raises(exceptions.BuildBlockedError):
        with misc.block_compile():
            self.make()
    str(exceptions.BuildBlockedError())

    assert self.make()
コード例 #17
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_macos_arches(monkeypatch):
    """Test cross compiling for arm64/x86_64 on macOS."""

    if platform.system() != "Darwin":
        return
    xcode = _xcode_version()
    if xcode is None or xcode < (12, 2):
        pytest.skip("Needs Xcode >= 12.2")

    # The test C code should require linking to be meaningful.
    self = CSlug(anchor(name()), io.StringIO("""\
        #include <math.h>
        #include <stdlib.h>

        double take_sin(double x) { return sin(x); }
    """), links="m")  # yapf: disable

    for arches in ["x86_64", "arm64", "x86_64 arm64"]:
        monkeypatch.setenv("MACOS_ARCHITECTURE", arches)

        # Try compiling.
        try:
            self.make()
        except exceptions.BuildError:
            # This is unlikely to be cslug specific. More likely its Xcode
            # getting into a mess.
            pytest.skip("This compiler appears not to be setup to build cross "
                        "arch binaries.")

        # Verify which type(s) are included in the binary we just built using
        # lipo - a builtin command line tool for inspecting, slicing and making
        # fat binaries.
        _arches = run(["lipo", "-archs", str(self.path)],
                      stdout=PIPE).stdout.decode()
        assert sorted(arches.split()) == sorted(_arches.split())

        # If we just built a native binary or a dual binary then we can run it
        # and it should work.
        if platform.machine() in arches:
            self.dll.take_sin(2) == pytest.approx(math.sin(2))
コード例 #18
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_infinite_math():
    # Assert that the `-ffinite-math-only` optimizer flag (or something similar)
    # is not enabled.
    # This is why -Ofast should not be used.

    self = CSlug(anchor(name(), io.StringIO("""\
        #include <math.h>

        int is_nan(double x) { return isnan(x); }
        int is_finite(double x) { return isfinite(x); }

    """)))  # yapf: disable

    assert self.dll.is_nan(12) == 0
    assert self.dll.is_finite(12) == 1

    assert self.dll.is_nan(math.inf) == 0
    assert self.dll.is_finite(math.inf) == 0

    # Bizarrely, Windows gcc but not tcc returns -1 instead of 1.
    assert self.dll.is_nan(math.nan) in (-1, 1)
    assert self.dll.is_finite(math.nan) == 0
コード例 #19
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_custom_compiler_flags():
    """Assert that custom flags appear and in the right place."""

    self = CSlug(name(), "file.c", flags="-some-flag", links="some-library")

    old = os.environ.copy()
    try:
        os.environ["CC_FLAGS"] = "-env-flag1 -env-flag2"
        cmd = self.compile_command()[0]
    finally:
        os.environ.clear()
        os.environ.update(old)

    # The order must be defaults, CSlug(flags=...) flags, CC_FLAGS, files,
    # linker options.

    assert cmd.index("-Wall") \
         < cmd.index("-some-flag") \
         < cmd.index("-env-flag1") \
         < cmd.index("-env-flag2") \
         < cmd.index("file.c") \
         < cmd.index("-lsome-library")
コード例 #20
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_contradictory_flags():
    """Assert that compilers accept contradictory flags without complaint."""

    # Should warn for implicit function declaration due to missing
    # #include <math.h>.
    self = CSlug(anchor(name()),
                 io.StringIO("double foo() { return sin(3); }"))

    try:
        with pytest.warns(exceptions.BuildWarning, match="implicit.*"):
            self.make()
    except exceptions.BuildError as ex:
        # The default clang on macOS elevates this to an error with -Werror.
        # Also fixable with flags="-Wno-error=implicit-function-declaration".
        assert "implicit" in str(ex)

    # Specify no warnings to override the default of all warnings -Wall.
    self.flags.append("-w")

    with warnings.catch_warnings():
        warnings.filterwarnings("error", category=exceptions.BuildWarning)
        self.make()
コード例 #21
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_buffers_or_temporary_files():
    """Test CSlug.compile_command() only with both pseup and temporary files."""
    self = CSlug(*anchor(name(), io.StringIO("foo"), io.StringIO("bar")))

    # gcc does support pseudo files and therefore they should be used.
    command, buffers, temporary_files = \
        self.compile_command(_cc_version=("gcc", (10,)))
    assert "-" in command
    assert len(buffers) == 2
    assert len(temporary_files) == 0
    assert buffers[0].read() == "foo"
    for option in command:
        assert not option.endswith(".c")

    # pcc doesn't support pseudo files so temporary files must be used instead.
    command, buffers, temporary_files = \
        self.compile_command(_cc_version=("pcc", (1,)))
    assert "-" not in command
    assert len(buffers) == 0
    assert len(temporary_files) == 2
    assert temporary_files[0].name in command
    assert os.path.exists(temporary_files[0].name)
    assert temporary_files[0].name.endswith(".c")
コード例 #22
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_names_not_in_dll():
    """
    Check that CSlug doesn't get into too much of a mess if it thinks a
    function should exist but doesn't.
    """

    # Both the functions in the C source below look to cslug like they would be
    # included in the DLL but in fact aren't.
    self = CSlug(*anchor(name(), io.StringIO("""

        inline int add_1(int x) { return x + 1; }

        #if 0
        float times_2(float y) { return y * 2.0; }
        #endif

    """))) # yapf: disable

    # Ensure built:
    self.dll

    # Check cslug found them.
    assert "add_1" in self.types_map.functions
    assert "times_2" in self.types_map.functions

    # But they are not in the DLL.

    # Inline functions may still be present. I believe this is gcc version
    # dependent.
    # assert not hasattr(self.dll, "add_1")
    # with pytest.raises(AttributeError):
    #     self.dll.add_1

    assert not hasattr(self.dll, "times_2")
    with pytest.raises(AttributeError):
        self.dll.time_2
コード例 #23
0
from cslug import CSlug, anchor

slug = CSlug(anchor("person.c"))

slug.make()
コード例 #24
0
import os

os.chdir(os.path.dirname(__file__))

import ctypes
from cslug import CSlug, ptr

slug = CSlug("${FILE_NAME}.c")
コード例 #25
0
ファイル: _ragged_array.py プロジェクト: bwoodsend/rockhopper
import ctypes
import sys

import numpy as np
from cslug import CSlug, ptr, anchor, Header

from rockhopper import RequestMeError

NUMPY_REPR = False

BIG_ENDIAN = sys.byteorder == "big"
endians_header = Header(*anchor("src/endians.h", "src/endians.c"),
                        includes=["<stdbool.h>", '"_endian_typedefs.h"'])
slug = CSlug(anchor(
    "_slugs/ragged_array",
    "src/ragged_array.c",
    "src/ragged_array.h",
    "src/endians.c",
), headers=endians_header)  # yapf: disable

dtype_like = Union[np.dtype, Type[np.generic]]


def prod(iterable):
    """Equivalent to :func:`math.prod` introduced in Python 3.8. """
    out = 1
    for i in iterable:
        out = out * i
    return out


class RaggedArray(object):
コード例 #26
0
ファイル: test_slugs.py プロジェクト: bwoodsend/cslug
def test_remake():
    from cslug._stdlib import dlclose, null_free_dll, stdlib
    assert dlclose is not null_free_dll, \
        "A `dlclose()` function hasn't been found for this platform. It "\
        "should be added to `_cslug._stdlib.py`."

    slug = CSlug(anchor(name(), RESOURCES / "basic.c"))
    path = slug.dll._name

    # Having the DLL open should block writing to it on Windows.
    ref = ctypes.CDLL(path)

    try:
        slug.make()
    except exceptions.LibraryOpenElsewhereError as ex:
        # This will happen only on Windows.
        assert path in str(ex)

    if platform.system() == "FreeBSD":
        # dlclose() seems to complain no matter what I do with it.
        stdlib.dlerror.restype = ctypes.c_char_p
        # This fails with an unhelpful b"Service unavailable".
        # assert dlclose(ctypes.c_void_p(ref._handle)) == 0, stdlib.dlerror()
        # dlclosing is mainly for Windows, which causes Permission errors if
        # you forget it. As far as I know, this issue is harmless.
    else:
        assert dlclose(ctypes.c_void_p(ref._handle)) == 0

    # With the DLL closed make() should work.
    slug.make()

    # Each slug gets registered in `_slug_refs`. Check that this has happened.
    # `slug` should be the only one registered under this filename.
    from cslug._cslug import _slug_refs
    assert slug.path in _slug_refs
    assert len(_slug_refs[slug.path]) == 1
    assert _slug_refs[slug.path][0]() is slug

    # Create another slug with the same filename. It should join the 1st in the
    # register.
    other = CSlug(slug.name, *slug.sources)
    assert other.path == slug.path
    assert len(_slug_refs[slug.path]) == 2
    assert _slug_refs[slug.path][1]() is other

    # Get `other` to open its DLL.
    other_dll = other.dll
    assert other_dll.add_1(2) == 3
    # Make `slug` try to rebuild the DLL which is already opened by `other`.
    # This would normally cause mayhem but doesn't because `slug.make()`
    # implicitly calls`other.close()` so that it doesn't try to overwrite an
    # open file.
    slug.make()
    # `other.dll` should re-open automatically.
    assert other.dll is not other_dll

    # `other` is weakref-ed and should still be garbage-collectable despite
    # being in `_slug_refs`.
    del other_dll, other
    # Test the link in `_slug_refs` is dead.
    assert _slug_refs[slug.path][1]() is None

    # `_slug_refs` should be cleared of dead links on calling `close()`.
    slug.close()
    assert len(_slug_refs[slug.path]) == 1
コード例 #27
0
def test_escaped_unicode_literal(source):
    """Test a C source file may contain string literals containing escaped
    unicode characters."""
    self = CSlug(anchor(name()), source)
    self.make()
    assert self.dll.a() == "㟐"
コード例 #28
0
def test_unicode_dll_path():
    """Test the filename to a DLL may contain unicode."""
    self = CSlug(DUMP / ("㟐㟐㟐" + name().name), RESOURCES / "basic.c")
    self.make()
    assert self.dll.add_1(-3) == -2
コード例 #29
0
def test_string_count_make():
    slug = CSlug(DEMOS / "strings" / "strings-demo.c")
    slug.make()
    global string_count
    string_count = slug
コード例 #30
0
ファイル: reverse.py プロジェクト: bwoodsend/cslug
import os

os.chdir(os.path.dirname(__file__))

import ctypes
from cslug import CSlug

slug = CSlug("reverse.c")
slug.make()

in_ = "Reverse this string."

out = ctypes.create_unicode_buffer(len(in_))
slug.dll.reverse(in_, out, len(in_))

assert out.value == in_[::-1]


def reverse(in_):
    """
    Returns a :class:`str` in reverse order.
    """
    out = ctypes.create_unicode_buffer(len(in_))
    slug.dll.reverse(in_, out, len(in_))
    return out.value


assert reverse(in_) == in_[::-1]