Skip to content

Commit c7ec111

Browse files
committed
Add some tuple pbt tests
1 parent 67120f7 commit c7ec111

File tree

6 files changed

+308
-4
lines changed

6 files changed

+308
-4
lines changed

.github/workflows/unit_tests.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ jobs:
123123
- name: Install build tools
124124
run: |
125125
${{ matrix.install }}
126-
sudo apt install -y ninja-build
126+
sudo apt install -y ninja-build python3-pip
127+
sudo pip3 install --upgrade pip
128+
sudo pip3 install pytest pytest-forked hypothesis
127129
128130
- name: Restore CPM cache
129131
env:
@@ -238,7 +240,9 @@ jobs:
238240
- name: Install build tools
239241
run: |
240242
${{ matrix.install }}
241-
sudo apt install -y ninja-build
243+
sudo apt install -y ninja-build python3-pip
244+
sudo pip3 install --upgrade pip
245+
sudo pip3 install pytest pytest-forked hypothesis
242246
243247
- name: Restore CPM cache
244248
env:
@@ -285,7 +289,9 @@ jobs:
285289
- name: Install build tools
286290
run: |
287291
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
288-
sudo apt update && sudo apt install -y gcc-${{env.DEFAULT_GCC_VERSION}} g++-${{env.DEFAULT_GCC_VERSION}} ninja-build valgrind
292+
sudo apt update && sudo apt install -y gcc-${{env.DEFAULT_GCC_VERSION}} g++-${{env.DEFAULT_GCC_VERSION}} ninja-build valgrind python3-pip
293+
sudo pip3 install --upgrade pip
294+
sudo pip3 install pytest pytest-forked hypothesis
289295
290296
- name: Restore CPM cache
291297
env:
@@ -357,7 +363,9 @@ jobs:
357363
- name: Install build tools
358364
run: |
359365
wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh ${{env.MULL_LLVM_VERSION}}
360-
sudo apt install -y ninja-build
366+
sudo apt install -y ninja-build python3-pip
367+
sudo pip3 install --upgrade pip
368+
sudo pip3 install pytest pytest-forked hypothesis
361369
362370
- name: Install mull
363371
env:

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@
1010
.cmake-format.yaml
1111
CMakePresets.json
1212
/toolchains
13+
__pycache__
14+
.mypy_cache
15+
.pytest_cache
16+
.hypothesis

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,4 @@ if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20)
6464
endif()
6565

6666
add_subdirectory(fail)
67+
add_subdirectory(pbt)

test/pbt/CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
get_target_property(STDX_INCLUDE_DIRS stdx INTERFACE_INCLUDE_DIRECTORIES)
2+
get_target_property(SANITIZER_ARGS sanitizers INTERFACE_COMPILE_OPTIONS)
3+
4+
if(SANITIZER_ARGS AND NOT SANITIZER_ARGS STREQUAL "SANITIZER_ARGS-NOTFOUND")
5+
message(STATUS "Target my_target has SANITIZER_ARGS: ${SANITIZER_ARGS}")
6+
set(SANITIZER_ARGS "--compiler-args \"${SANITIZER_ARGS}\"")
7+
else()
8+
message(
9+
STATUS "Target my_target does not have SANITIZER_ARGS or it is not set")
10+
set(SANITIZER_ARGS "")
11+
endif()
12+
13+
string(REPLACE ";" "," STDX_INCLUDE_DIRS_ESCAPED "${STDX_INCLUDE_DIRS}")
14+
15+
if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20)
16+
add_unit_test(
17+
tuple
18+
PYTEST
19+
FILES
20+
tuple.py
21+
EXTRA_ARGS
22+
-vv
23+
--compiler
24+
${CMAKE_CXX_COMPILER}
25+
${SANITIZER_ARGS}
26+
--includes
27+
\"${STDX_INCLUDE_DIRS_ESCAPED}\")
28+
endif()

test/pbt/conftest.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import pytest
2+
import hypothesis
3+
import subprocess
4+
import tempfile
5+
import os
6+
7+
hypothesis.settings.register_profile("ci", max_examples=500)
8+
hypothesis.settings.register_profile("fast", max_examples=10)
9+
10+
11+
def pytest_addoption(parser):
12+
parser.addoption("--includes", action="store", help="C++ include directories", required=True)
13+
parser.addoption("--compiler", action="store", help="C++ compiler", required=True)
14+
parser.addoption("--compiler-args", action="store", help="C++ compiler arguments", default="", required=False)
15+
16+
17+
@pytest.fixture(scope="module")
18+
def compiler(pytestconfig):
19+
return pytestconfig.getoption("compiler")
20+
21+
@pytest.fixture(scope="module")
22+
def include_dirs(pytestconfig):
23+
return [i for i in pytestconfig.getoption("includes").split(",") if i]
24+
25+
@pytest.fixture(scope="module")
26+
def compiler_args(pytestconfig):
27+
args = pytestconfig.getoption("compiler_args")
28+
29+
if args:
30+
return [i for i in args.split(" ") if i]
31+
else:
32+
return []
33+
34+
@pytest.fixture(scope="module")
35+
def compile(compiler, compiler_args, include_dirs):
36+
include_args = [f"-I{i}" for i in include_dirs]
37+
def f(code_str):
38+
with tempfile.NamedTemporaryFile(delete=False, suffix=".cpp") as temp_cpp_file:
39+
temp_cpp_file.write(code_str.encode('utf-8'))
40+
temp_cpp_file_path = temp_cpp_file.name
41+
42+
try:
43+
compile_command = [
44+
compiler, temp_cpp_file_path,
45+
"-o", temp_cpp_file_path + ".out",
46+
"--std=c++20",
47+
"-fbracket-depth=99999"
48+
] + compiler_args + include_args
49+
50+
result = subprocess.run(compile_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
51+
52+
if result.returncode == 0:
53+
return True
54+
else:
55+
print(f"Compiler returned non-zero exit code: {result.returncode}")
56+
print(result.stderr.decode('utf-8'))
57+
print(result.stdout.decode('utf-8'))
58+
return False
59+
60+
except e:
61+
print(f"An error occurred: {e}")
62+
print(result.stderr.decode('utf-8'))
63+
print(result.stdout.decode('utf-8'))
64+
return False
65+
66+
finally:
67+
os.remove(temp_cpp_file_path)
68+
if os.path.exists(temp_cpp_file_path + ".out"):
69+
os.remove(temp_cpp_file_path + ".out")
70+
71+
return f
72+

test/pbt/tuple.py

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
from hypothesis import strategies as st, given, settings, event, assume
2+
3+
def unpack(l):
4+
return ", ".join([str(i) for i in l])
5+
6+
small_ints = st.integers(min_value=-100, max_value=100)
7+
8+
@st.composite
9+
def tuples(draw, children):
10+
values = draw(st.lists(children))
11+
return f"stdx::make_tuple({unpack(values)})"
12+
13+
@st.composite
14+
def list_trees(draw, leaves=st.integers()):
15+
l = draw(st.recursive(leaves, lambda children: st.lists(children)))
16+
if not isinstance(l, list):
17+
l = [l]
18+
return l
19+
20+
def as_tuple_tree(value):
21+
if isinstance(value, list):
22+
values = [as_tuple_tree(v) for v in value]
23+
return f"stdx::make_tuple({unpack(values)})"
24+
else:
25+
return value
26+
27+
@st.composite
28+
def tuple_trees(draw, leaves=st.integers()):
29+
return draw(st.recursive(leaves, lambda children: tuples(children)))
30+
31+
@settings(deadline=10000)
32+
@given(tuple_trees(small_ints))
33+
def test_tuple_trees(compile, t):
34+
assert compile(f"""
35+
#include <stdx/tuple.hpp>
36+
37+
constexpr auto t = {t};
38+
39+
int main() {{
40+
return 0;
41+
}}
42+
""")
43+
44+
@settings(deadline=10000)
45+
@given(list_trees(small_ints))
46+
def test_tuple_size(compile, l):
47+
t = as_tuple_tree(l)
48+
assert compile(f"""
49+
#include <stdx/tuple.hpp>
50+
51+
constexpr auto t = {t};
52+
static_assert(stdx::tuple_size_v<decltype(t)> == {len(l)});
53+
static_assert(std::size(t) == {len(l)});
54+
55+
int main() {{
56+
return 0;
57+
}}
58+
""")
59+
60+
61+
@settings(deadline=10000)
62+
@given(list_trees(small_ints), st.integers())
63+
def test_get_by_index(compile, l, i):
64+
assume(len(l) > 0)
65+
t = as_tuple_tree(l)
66+
i = i % len(l)
67+
68+
expected_v = as_tuple_tree(l[i])
69+
70+
assert compile(f"""
71+
#include <stdx/tuple.hpp>
72+
73+
using namespace stdx::literals;
74+
75+
constexpr auto t = {t};
76+
constexpr auto expected = {expected_v};
77+
78+
static_assert(stdx::get<{i}>(t) == expected);
79+
static_assert(get<{i}>(t) == expected);
80+
81+
static_assert(t[{i}_idx] == expected);
82+
static_assert(t[stdx::index<{i}>] == expected);
83+
84+
// TODO: enable once #148 is fixed
85+
//static_assert(t.get(stdx::index<{i}>) == expected);
86+
//static_assert(t.get({i}_idx) == expected);
87+
88+
int main() {{
89+
return 0;
90+
}}
91+
""")
92+
93+
94+
@settings(deadline=10000)
95+
@given(st.lists(list_trees(small_ints)))
96+
def test_tuple_cat(compile, ls):
97+
ts = [as_tuple_tree(l) for l in ls]
98+
99+
flattened_ls = [i for subl in ls for i in subl]
100+
expected = as_tuple_tree(flattened_ls)
101+
102+
assert compile(f"""
103+
#include <stdx/tuple.hpp>
104+
#include <stdx/tuple_algorithms.hpp>
105+
106+
static_assert(stdx::tuple_cat({unpack(ts)}) == {expected});
107+
108+
int main() {{
109+
return 0;
110+
}}
111+
""")
112+
113+
114+
@settings(deadline=10000)
115+
@given(list_trees(small_ints), st.one_of(list_trees(small_ints), small_ints))
116+
def test_push(compile, l, elem):
117+
expected_back = as_tuple_tree(l + [elem])
118+
expected_front = as_tuple_tree([elem] + l)
119+
120+
if isinstance(elem, list):
121+
elem = as_tuple_tree(elem)
122+
else:
123+
elem = str(elem)
124+
125+
t = as_tuple_tree(l)
126+
127+
assert compile(f"""
128+
#include <stdx/tuple.hpp>
129+
#include <stdx/tuple_algorithms.hpp>
130+
131+
constexpr auto t = {t};
132+
constexpr auto elem = {elem};
133+
134+
constexpr auto expected_back = {expected_back};
135+
static_assert(stdx::tuple_push_back(t, elem) == expected_back);
136+
static_assert(stdx::tuple_snoc(t, elem) == expected_back);
137+
138+
constexpr auto expected_front = {expected_front};
139+
static_assert(stdx::tuple_push_front(elem, t) == expected_front);
140+
static_assert(stdx::tuple_cons(elem, t) == expected_front);
141+
142+
int main() {{
143+
return 0;
144+
}}
145+
""")
146+
147+
from itertools import product
148+
149+
@settings(deadline=20000)
150+
@given(st.lists(list_trees(small_ints), max_size=3))
151+
def test_cartesian_product(compile, ls):
152+
expected = as_tuple_tree([list(p) for p in product(*ls)])
153+
154+
ts = [as_tuple_tree(l) for l in ls]
155+
156+
assert compile(f"""
157+
#include <stdx/tuple.hpp>
158+
#include <stdx/tuple_algorithms.hpp>
159+
160+
static_assert(stdx::cartesian_product_copy({unpack(ts)}) == {expected});
161+
162+
int main() {{
163+
return 0;
164+
}}
165+
""")
166+
167+
from functools import reduce
168+
169+
@settings(deadline=20000)
170+
@given(st.lists(small_ints))
171+
def test_star_of(compile, l):
172+
expected_any_of = any([i > 50 for i in l])
173+
expected_all_of = all([i > 50 for i in l])
174+
expected_none_of = not expected_any_of
175+
176+
t = as_tuple_tree(l)
177+
178+
assert compile(f"""
179+
#include <stdx/tuple.hpp>
180+
#include <stdx/tuple_algorithms.hpp>
181+
182+
constexpr auto f = [](int i) {{ return i > 50; }};
183+
184+
static_assert(stdx::any_of(f, {t}) == {str(expected_any_of).lower()});
185+
static_assert(stdx::all_of(f, {t}) == {str(expected_all_of).lower()});
186+
static_assert(stdx::none_of(f, {t}) == {str(expected_none_of).lower()});
187+
188+
int main() {{
189+
return 0;
190+
}}
191+
""")

0 commit comments

Comments
 (0)