Skip to content

Commit

Permalink
add pbt for lookup library
Browse files Browse the repository at this point in the history
  • Loading branch information
lukevalenty committed Oct 15, 2024
1 parent 962740f commit cab8974
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 4 deletions.
24 changes: 20 additions & 4 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ jobs:
- name: Install build tools
run: |
${{ matrix.install }}
sudo apt install -y ninja-build
sudo apt install -y ninja-build python3-venv python3-pip
python3 -m venv ${{github.workspace}}/test_venv
source ${{github.workspace}}/test_venv/bin/activate
pip install -r ${{github.workspace}}/requirements.txt
echo "${{github.workspace}}/test_venv/bin" >> $GITHUB_PATH
- name: Restore CPM cache
env:
Expand Down Expand Up @@ -176,7 +180,11 @@ jobs:
- name: Install build tools
run: |
${{ matrix.install }}
sudo apt install -y ninja-build
sudo apt install -y ninja-build python3-venv python3-pip
python3 -m venv ${{github.workspace}}/test_venv
source ${{github.workspace}}/test_venv/bin/activate
pip install -r ${{github.workspace}}/requirements.txt
echo "${{github.workspace}}/test_venv/bin" >> $GITHUB_PATH
- name: Restore CPM cache
env:
Expand Down Expand Up @@ -296,7 +304,11 @@ jobs:
- name: Install build tools
run: |
${{ matrix.install }}
sudo apt install -y ninja-build
sudo apt install -y ninja-build python3-venv python3-pip
python3 -m venv ${{github.workspace}}/test_venv
source ${{github.workspace}}/test_venv/bin/activate
pip install -r ${{github.workspace}}/requirements.txt
echo "${{github.workspace}}/test_venv/bin" >> $GITHUB_PATH
- name: Restore CPM cache
env:
Expand Down Expand Up @@ -342,7 +354,11 @@ jobs:

- name: Install build tools
run: |
sudo apt update && sudo apt install -y gcc-${{env.DEFAULT_GCC_VERSION}} g++-${{env.DEFAULT_GCC_VERSION}} ninja-build valgrind
sudo apt update && sudo apt install -y gcc-${{env.DEFAULT_GCC_VERSION}} g++-${{env.DEFAULT_GCC_VERSION}} ninja-build valgrind python3-venv python3-pip
python3 -m venv ${{github.workspace}}/test_venv
source ${{github.workspace}}/test_venv/bin/activate
pip install -r ${{github.workspace}}/requirements.txt
echo "${{github.workspace}}/test_venv/bin" >> $GITHUB_PATH
- name: Restore CPM cache
env:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@
CMakePresets.json
/toolchains
mull.yml
__pycache__
.mypy_cache
.pytest_cache
.hypothesis
11 changes: 11 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pytest==8.3.3
pytest-forked==1.6.0
pytest-xdist==3.6.1
hypothesis==6.112.5
attrs==24.2.0
execnet==2.1.1
pluggy==1.5.0
sortedcontainers==2.4.0
iniconfig==2.0.0
packaging==24.1
py==1.11.0
2 changes: 2 additions & 0 deletions test/lookup/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,5 @@ foreach(BENCH_ALG_NAME ${BENCH_ALG_NAMES})
QBENCH_DATASET="${BENCH_DATASET}")
endforeach()
endforeach()

add_subdirectory(pbt)
16 changes: 16 additions & 0 deletions test/lookup/pbt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
add_executable(pbt_prototype_driver EXCLUDE_FROM_ALL pbt_prototype_driver.cpp)
target_link_libraries(pbt_prototype_driver PUBLIC sanitizers warnings cib)
target_compile_options(pbt_prototype_driver PUBLIC -fconstexpr-steps=4000000000)

add_unit_test(
tuple
PYTEST
FILES
lookup.py
EXTRA_ARGS
-vv
-n
auto
-x
--compile-commands=${CMAKE_BINARY_DIR}/compile_commands.json
--prototype-driver=${CMAKE_CURRENT_SOURCE_DIR}/pbt_prototype_driver.cpp)
98 changes: 98 additions & 0 deletions test/lookup/pbt/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import pytest
import hypothesis
import json
import subprocess
import tempfile
import os
import re

hypothesis.settings.register_profile("ci", max_examples=500)
hypothesis.settings.register_profile("fast", max_examples=10)



def pytest_addoption(parser):
parser.addoption("--compiler", action="store", help="C++ compiler", default=None, required=False)
parser.addoption("--compiler-args", action="store", help="C++ compiler arguments", default="", required=False)
parser.addoption("--includes", action="store", help="C++ include directories", default="", required=False)

parser.addoption("--compile-commands", action="store", help="cmake compiler commands", default=None, required=False)
parser.addoption("--prototype-driver", action="store", help="Prototype .cpp filename to gather compilation command from", default=None, required=False)

@pytest.fixture(scope="module")
def cmake_compilation_command(pytestconfig):
compile_commands_filename = pytestconfig.getoption("compile_commands")
prototype_driver_filename = pytestconfig.getoption("prototype_driver")

if compile_commands_filename is None or prototype_driver_filename is None:
return None

def f(filename):
with open(compile_commands_filename, "r") as f:
db = json.load(f)
for obj in db:
if obj["file"] == prototype_driver_filename:
cmd = obj["command"]
cmd = cmd.replace(prototype_driver_filename, filename)
cmd = re.sub(r"-o .*?\.cpp\.o", f"-o {filename}.o", cmd)
cmd = re.sub(r" -c ", f" ", cmd)
return cmd.split(" ")

return f

@pytest.fixture(scope="module")
def args_compilation_command(pytestconfig):
compiler = pytestconfig.getoption("compiler")
if compiler is None:
return None

include_dirs = [f"-I{i}" for i in pytestconfig.getoption("includes").split(",") if i]
compiler_args = [i for i in pytestconfig.getoption("compiler_args").split(",") if i]

def f(filename):
compile_command = [
compiler, temp_cpp_file_path,
"-o", temp_cpp_file_path + ".out"
] + compiler_args + include_args
return compile_command

return f



@pytest.fixture(scope="module")
def compile(cmake_compilation_command, args_compilation_command):
cmd = cmake_compilation_command
if cmd is None:
cmd = args_compilation_command

def f(code_str):
code_str += "\n"
with tempfile.NamedTemporaryFile(delete=False, suffix=".cpp") as temp_cpp_file:
temp_cpp_file.write(code_str.encode('utf-8'))
temp_cpp_file_path = temp_cpp_file.name

try:
compile_command = cmd(temp_cpp_file_path)
result = subprocess.run(compile_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

if result.returncode == 0:
os.chmod(temp_cpp_file_path + ".o", 0o700)
return temp_cpp_file_path + ".o"
else:
error_message = (
f"Compiler returned non-zero exit code: {result.returncode}\n"
f"Compilation command: {' '.join(compile_command)}\n"
f"Source code:\n{code_str}\n"
f"Compiler stderr:\n{result.stderr.decode('utf-8')}\n"
f"Compiler stdout:\n{result.stdout.decode('utf-8')}\n"
)
pytest.fail(error_message)

except Exception as e:
pytest.fail(str(e))
finally:
os.remove(temp_cpp_file_path)

return f

76 changes: 76 additions & 0 deletions test/lookup/pbt/lookup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from hypothesis import strategies as st, given, settings, event, assume
import subprocess

def unpack(l):
return ", ".join([str(i) for i in l])

uint16s = st.integers(min_value = 0, max_value = (1 << 16) - 1)


def lookup_make(lookup_type, key_type, value_type, default, entries):
entries = [f"lookup::entry<{key_type}, {value_type}>{{{k}, {v}}}" for k, v in entries]
return f"""
{lookup_type}::make(CX_VALUE(lookup::input<{key_type}, {value_type}, {len(entries)}>{{
{default},
std::array<lookup::entry<{key_type}, {value_type}>, {len(entries)}>{{ {unpack(entries)} }}
}}))
"""

@st.composite
def lookup_inputs(draw, keys=uint16s, values=uint16s, default=uint16s, min_size=0, max_size=10):
entries = draw(st.lists(st.tuples(keys, values), min_size=min_size, max_size=max_size, unique_by=lambda x: x[0]))
default = draw(default)
return (default, entries)

pseudo_pext_lookups = st.sampled_from([
"lookup::pseudo_pext_lookup<>",
"lookup::pseudo_pext_lookup<true, 1>",
"lookup::pseudo_pext_lookup<true, 2>",
"lookup::pseudo_pext_lookup<true, 3>",
"lookup::pseudo_pext_lookup<true, 4>"
])

@settings(deadline=50000)
@given(
pseudo_pext_lookups,
lookup_inputs(min_size=2, max_size=12),
st.lists(uint16s, min_size=10, max_size=1000, unique=True)
)
def test_lookup(compile, t, l, extras):
default, entries = l

lookup_model = {k: v for k, v in entries}
lookup = lookup_make(t, "std::uint16_t", "std::uint16_t", default, entries)

check_keys = set(lookup_model.keys()).union(set(extras))

static_asserts = "\n".join(
[f"static_assert(lookup[{k}] == {lookup_model.get(k, default)});" for k in check_keys]
)

runtime_checks = " &&\n".join(
[f"(lookup[{k}] == {lookup_model.get(k, default)})" for k in check_keys]
)

out = compile(f"""
#include <lookup/entry.hpp>
#include <lookup/input.hpp>
#include <lookup/lookup.hpp>
#include <stdx/utility.hpp>
#include <array>
#include <cstdint>
int main() {{
[[maybe_unused]] constexpr auto lookup = {lookup};
{static_asserts}
bool const pass = {runtime_checks};
return pass ? 0 : 1;
}}
""")

result = subprocess.run([out], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
assert result.returncode == 0
1 change: 1 addition & 0 deletions test/lookup/pbt/pbt_prototype_driver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int main() {}

0 comments on commit cab8974

Please sign in to comment.