Skip to content

Commit 38d9d21

Browse files
committed
Basic unit tests for stopping solvers
1 parent 8c26311 commit 38d9d21

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed

test/unit/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,11 @@ target_sources(ArraysTest
243243

244244
target_link_libraries(ArraysTest OpenSMT gtest gtest_main)
245245
gtest_add_tests(TARGET ArraysTest)
246+
247+
add_executable(StopTest)
248+
target_sources(StopTest
249+
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/test_Stop.cc"
250+
)
251+
252+
target_link_libraries(StopTest OpenSMT gtest gtest_main)
253+
gtest_add_tests(TARGET StopTest)

test/unit/test_Stop.cc

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Copyright (c) 2025, Kolarik Tomas <tomaqa@gmail.com>
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*
6+
*/
7+
8+
#include <gtest/gtest.h>
9+
10+
#include <api/MainSolver.h>
11+
#include <logics/ArithLogic.h>
12+
13+
#include <array>
14+
#include <chrono>
15+
#include <future>
16+
#include <thread>
17+
18+
using namespace std::chrono_literals;
19+
20+
namespace opensmt {
21+
22+
constexpr auto const sleep_amount = 10ms;
23+
24+
class StopTest : public ::testing::Test {
25+
protected:
26+
static constexpr size_t nSolvers = 2;
27+
28+
void init() {
29+
for (size_t i = 0; i < nSolvers; ++i) {
30+
auto & logic = logics[i];
31+
auto & solver = solvers[i];
32+
33+
PTRef x = logic.mkRealVar("x");
34+
PTRef y = logic.mkRealVar("y");
35+
PTRef z = logic.mkRealVar("z");
36+
PTRef w = logic.mkRealVar("w");
37+
PTRef v = logic.mkRealVar("v");
38+
39+
// This at least ensures that it is not solved just during the preprocessing phase
40+
41+
solver.addAssertion(logic.mkOr(logic.mkEq(x, y), logic.mkEq(x, z)));
42+
43+
solver.addAssertion(logic.mkOr(logic.mkEq(y, w), logic.mkEq(y, v)));
44+
solver.addAssertion(logic.mkOr(logic.mkEq(y, w), logic.mkEq(y, v)));
45+
46+
solver.addAssertion(logic.mkOr(logic.mkEq(z, w), logic.mkEq(z, v)));
47+
solver.addAssertion(logic.mkOr(logic.mkEq(z, w), logic.mkEq(z, v)));
48+
49+
solver.addAssertion(logic.mkLt(x, w));
50+
solver.addAssertion(logic.mkLt(x, v));
51+
}
52+
}
53+
54+
void solveAllOnBackground() {
55+
for (size_t i = 0; i < nSolvers; ++i) {
56+
solveOnBackground(i);
57+
}
58+
}
59+
60+
void notifyAll() {
61+
for (size_t i = 0; i < nSolvers; ++i) {
62+
solvers[i].notifyStop();
63+
}
64+
}
65+
66+
std::array<sstat, nSolvers> waitAll() {
67+
std::array<sstat, nSolvers> results;
68+
for (size_t i = 0; i < nSolvers; ++i) {
69+
results[i] = wait(i);
70+
}
71+
return results;
72+
}
73+
74+
SMTConfig config{};
75+
std::array<ArithLogic, nSolvers> logics{Logic_t::QF_LRA, Logic_t::QF_LRA};
76+
std::array<MainSolver, nSolvers> solvers{MainSolver{logics[0], config, "solver 1"},
77+
MainSolver{logics[1], config, "solver 2"}};
78+
79+
std::array<std::thread, nSolvers> threads;
80+
std::array<std::future<sstat>, nSolvers> futures;
81+
82+
private:
83+
void runOnBackground(size_t idx, auto f) {
84+
std::packaged_task task{std::move(f)};
85+
futures[idx] = task.get_future();
86+
threads[idx] = std::thread{std::move(task)};
87+
}
88+
89+
void solveOnBackground(size_t idx) {
90+
runOnBackground(idx, [this, idx] {
91+
std::this_thread::sleep_for(sleep_amount);
92+
return solvers[idx].check();
93+
});
94+
}
95+
96+
sstat wait(size_t idx) {
97+
threads[idx].join();
98+
return futures[idx].get();
99+
}
100+
};
101+
102+
TEST_F(StopTest, test_NoStop) {
103+
init();
104+
105+
solveAllOnBackground();
106+
auto results = waitAll();
107+
108+
ASSERT_EQ(results, std::to_array({s_False, s_False}));
109+
}
110+
111+
TEST_F(StopTest, test_LocalPreStop) {
112+
init();
113+
114+
solvers.front().notifyStop();
115+
solveAllOnBackground();
116+
auto results = waitAll();
117+
118+
ASSERT_EQ(results, std::to_array({s_Undef, s_False}));
119+
}
120+
121+
TEST_F(StopTest, test_GlobalPreStop) {
122+
init();
123+
124+
notifyGlobalStop();
125+
solveAllOnBackground();
126+
auto results = waitAll();
127+
128+
ASSERT_EQ(results, std::to_array({s_Undef, s_Undef}));
129+
}
130+
131+
TEST_F(StopTest, test_AllLocalPreStop) {
132+
init();
133+
134+
notifyAll();
135+
solveAllOnBackground();
136+
auto results = waitAll();
137+
138+
ASSERT_EQ(results, std::to_array({s_Undef, s_Undef}));
139+
}
140+
141+
// Post-stops at least ensure the functionality of returning unknown after the solving is already trigerred
142+
143+
TEST_F(StopTest, test_LocalPostStop) {
144+
init();
145+
146+
solveAllOnBackground();
147+
std::this_thread::sleep_for(sleep_amount / 10);
148+
solvers.front().notifyStop();
149+
auto results = waitAll();
150+
151+
ASSERT_EQ(results, std::to_array({s_Undef, s_False}));
152+
}
153+
154+
TEST_F(StopTest, test_GlobalPostStop) {
155+
init();
156+
157+
solveAllOnBackground();
158+
std::this_thread::sleep_for(sleep_amount / 10);
159+
notifyGlobalStop();
160+
auto results = waitAll();
161+
162+
ASSERT_EQ(results, std::to_array({s_Undef, s_Undef}));
163+
}
164+
165+
TEST_F(StopTest, test_AllLocalPostStop) {
166+
init();
167+
168+
solveAllOnBackground();
169+
std::this_thread::sleep_for(sleep_amount / 10);
170+
notifyAll();
171+
auto results = waitAll();
172+
173+
ASSERT_EQ(results, std::to_array({s_Undef, s_Undef}));
174+
}
175+
176+
TEST_F(StopTest, test_LocalTimeout) {
177+
init();
178+
179+
solveAllOnBackground();
180+
if (futures.front().wait_for(sleep_amount / 2) != std::future_status::timeout) { ASSERT_TRUE(false); }
181+
182+
solvers.front().notifyStop();
183+
auto results = waitAll();
184+
185+
ASSERT_EQ(results, std::to_array({s_Undef, s_False}));
186+
}
187+
188+
TEST_F(StopTest, test_GlobalTimeout) {
189+
init();
190+
191+
solveAllOnBackground();
192+
if (futures.front().wait_for(sleep_amount / 2) != std::future_status::timeout) { ASSERT_TRUE(false); }
193+
194+
notifyGlobalStop();
195+
auto results = waitAll();
196+
197+
ASSERT_EQ(results, std::to_array({s_Undef, s_Undef}));
198+
}
199+
200+
TEST_F(StopTest, test_AllLocalTimeout) {
201+
init();
202+
203+
solveAllOnBackground();
204+
if (futures.front().wait_for(sleep_amount / 2) != std::future_status::timeout) { ASSERT_TRUE(false); }
205+
206+
notifyAll();
207+
auto results = waitAll();
208+
209+
ASSERT_EQ(results, std::to_array({s_Undef, s_Undef}));
210+
}
211+
212+
} // namespace opensmt

0 commit comments

Comments
 (0)