Skip to content

Commit 4fe6690

Browse files
NlightNFotisTGWDB
andcommitted
Add unit tests for the piped_processt class.
Add validation code for the `piped_processt` class, in the form of Catch unit tests, under `unit/util/`. Co-authored-by: Fotis Koutoulakis <[email protected]> Co-authored-by: Thomas Given-Wilson <[email protected]>
1 parent f27be89 commit 4fe6690

File tree

2 files changed

+257
-0
lines changed

2 files changed

+257
-0
lines changed

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ SRC += analyses/ai/ai.cpp \
123123
util/optional.cpp \
124124
util/optional_utils.cpp \
125125
util/parse_options.cpp \
126+
util/piped_process.cpp \
126127
util/pointer_offset_size.cpp \
127128
util/prefix_filter.cpp \
128129
util/range.cpp \

unit/util/piped_process.cpp

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/// \file
2+
/// \author Diffblue Ltd.
3+
/// Unit tests for checking the piped process communication mechanism.
4+
5+
#ifdef _WIN32
6+
// No unit tests yet!
7+
#else
8+
9+
# include <testing-utils/use_catch.h>
10+
# include <util/piped_process.h>
11+
12+
TEST_CASE(
13+
"We create a pipe and we can read from it",
14+
"[core][util][piped_process]")
15+
{
16+
const std::string to_be_echoed = "The Jabberwocky";
17+
// Need to give path to avoid shell built-in invocation
18+
const std::string binary = "/bin/echo";
19+
const std::string command = binary + " " + to_be_echoed;
20+
piped_processt process = piped_processt(command);
21+
22+
// This is an indirect way to detect when the pipe has something since
23+
// -1 is an infinite timeout wait. This could (in theory) also return
24+
// when there is an error, but this unit test is not doing error handling.
25+
process.can_receive(-1);
26+
std::string response = process.receive();
27+
28+
response.erase(
29+
std::remove(response.begin(), response.end(), '\n'), response.end());
30+
31+
REQUIRE(response == to_be_echoed);
32+
}
33+
34+
TEST_CASE(
35+
"We create a pipe, send and receive from it",
36+
"[core][util][piped_process]")
37+
{
38+
const std::string binary = "z3 -in";
39+
const std::string statement = "(echo \"hi\")\n";
40+
const std::string termination_statement = "(exit)\n";
41+
piped_processt process = piped_processt(binary);
42+
43+
piped_processt::process_send_responset res = process.send(statement);
44+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
45+
46+
process.can_receive(-1);
47+
std::string response = process.receive();
48+
response.erase(
49+
std::remove(response.begin(), response.end(), '\n'), response.end());
50+
REQUIRE(response == std::string("hi"));
51+
REQUIRE(response.length() == 2);
52+
53+
res = process.send(termination_statement);
54+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
55+
}
56+
57+
TEST_CASE("We create a pipe, interact", "[core][util][piped_process]")
58+
{
59+
const std::string binary = "z3 -in";
60+
std::string statement = "(echo \"hi\")\n";
61+
const std::string termination_statement = "(exit)\n";
62+
piped_processt process = piped_processt(binary);
63+
64+
piped_processt::process_send_responset res = process.send(statement);
65+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
66+
67+
process.can_receive(-1);
68+
std::string response = process.receive();
69+
response.erase(
70+
std::remove(response.begin(), response.end(), '\n'), response.end());
71+
REQUIRE(response == std::string("hi"));
72+
73+
statement = std::string("(echo \"Second string\")\n");
74+
res = process.send(statement);
75+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
76+
77+
process.can_receive(-1);
78+
response = process.receive();
79+
response.erase(
80+
std::remove(response.begin(), response.end(), '\n'), response.end());
81+
REQUIRE(response == std::string("Second string"));
82+
83+
res = process.send(termination_statement);
84+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
85+
}
86+
87+
TEST_CASE(
88+
"Use pipe to solve a simple SMT problem",
89+
"[core][util][piped_process]")
90+
{
91+
const std::string binary = "z3 -in -smt2";
92+
const std::string termination_statement = "(exit)\n";
93+
piped_processt process = piped_processt(binary);
94+
95+
piped_processt::process_send_responset res = process.send(
96+
"(set-logic QF_LIA) (declare-const x Int) (declare-const y Int) (assert (> "
97+
"(+ (mod x 4) (* 3 (div y 2))) (- x y))) (check-sat)\n");
98+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
99+
100+
process.can_receive(-1);
101+
std::string response = process.receive();
102+
response.erase(
103+
std::remove(response.begin(), response.end(), '\n'), response.end());
104+
REQUIRE(response == std::string("sat"));
105+
106+
res = process.send(termination_statement);
107+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
108+
}
109+
110+
TEST_CASE(
111+
"Use pipe to solve a simple SMT problem and get the model",
112+
"[core][util][piped_process]")
113+
{
114+
const std::string binary = "z3 -in -smt2";
115+
const std::string termination_statement = "(exit)\n";
116+
piped_processt process = piped_processt(binary);
117+
118+
piped_processt::process_send_responset res = process.send(
119+
"(set-logic QF_LIA) (declare-const x Int) (declare-const y Int) (assert (> "
120+
"(+ (mod x 4) (* 3 (div y 2))) (- x y))) (check-sat)\n");
121+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
122+
123+
process.can_receive(-1);
124+
std::string response = process.receive();
125+
response.erase(
126+
std::remove(response.begin(), response.end(), '\n'), response.end());
127+
REQUIRE(response == std::string("sat"));
128+
129+
res = process.send("(get-model)\n");
130+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
131+
132+
process.can_receive(-1);
133+
response = process.receive();
134+
// Since the above two lines will read IMMEDIATELY when data is available on
135+
// the pipe, it is likely that only the first line will be available.
136+
// Hence the check below is for the first line of the response.
137+
// Note that checking the whole model is done in another case below, and
138+
// the goal of the piped_process code is to enable interaction, parsing of
139+
// complex responses is left to the caller.
140+
REQUIRE(response.substr(0, 6) == std::string("(model"));
141+
142+
res = process.send(termination_statement);
143+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
144+
}
145+
146+
TEST_CASE(
147+
"Use pipe to solve a simple SMT problem with wait_receive",
148+
"[core][util][piped_process]")
149+
{
150+
const std::string binary = "z3 -in -smt2";
151+
const std::string termination_statement = "(exit)\n";
152+
piped_processt process = piped_processt(binary);
153+
154+
piped_processt::process_send_responset res = process.send(
155+
"(set-logic QF_LIA) (declare-const x Int) (declare-const y Int) (assert (> "
156+
"(+ (mod x 4) (* 3 (div y 2))) (- x y))) (check-sat)\n");
157+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
158+
159+
std::string response = process.wait_receive();
160+
response.erase(
161+
std::remove(response.begin(), response.end(), '\n'), response.end());
162+
REQUIRE(response == std::string("sat"));
163+
164+
res = process.send(termination_statement);
165+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
166+
}
167+
168+
TEST_CASE(
169+
"We create a pipe, interact, use wait_receivable",
170+
"[core][util][piped_process]")
171+
{
172+
const std::string binary = "z3 -in";
173+
std::string statement = "(echo \"hi\")\n";
174+
const std::string termination_statement = "(exit)\n";
175+
piped_processt process = piped_processt(binary);
176+
177+
piped_processt::process_send_responset res = process.send(statement);
178+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
179+
180+
process.wait_receivable(100);
181+
std::string response = process.receive();
182+
response.erase(
183+
std::remove(response.begin(), response.end(), '\n'), response.end());
184+
REQUIRE(response == std::string("hi"));
185+
186+
statement = std::string("(echo \"Second string\")\n");
187+
res = process.send(statement);
188+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
189+
190+
process.wait_receivable(100);
191+
response = process.receive();
192+
response.erase(
193+
std::remove(response.begin(), response.end(), '\n'), response.end());
194+
REQUIRE(response == std::string("Second string"));
195+
196+
res = process.send(termination_statement);
197+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
198+
}
199+
200+
TEST_CASE(
201+
"Use pipe to solve a simple SMT problem and get the model, with "
202+
"wait_receivable/can_receive",
203+
"[core][util][piped_process]")
204+
{
205+
const std::string binary = "z3 -in -smt2";
206+
const std::string termination_statement = "(exit)\n";
207+
piped_processt process = piped_processt(binary);
208+
209+
piped_processt::process_send_responset res = process.send(
210+
"(set-logic QF_LIA) (declare-const x Int) (declare-const y Int) (assert (> "
211+
"(+ (mod x 4) (* 3 (div y 2))) (- x y))) (check-sat)\n");
212+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
213+
214+
process.wait_receivable(100);
215+
std::string response = process.receive();
216+
response.erase(
217+
std::remove(response.begin(), response.end(), '\n'), response.end());
218+
REQUIRE(response == std::string("sat"));
219+
220+
res = process.send("(get-model)\n");
221+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
222+
223+
process.wait_receivable(500);
224+
response = process.receive();
225+
REQUIRE(
226+
response == std::string("(model \n (define-fun y () Int\n 0)\n "
227+
"(define-fun x () Int\n (- 4))\n)\n"));
228+
229+
res = process.send(termination_statement);
230+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
231+
}
232+
233+
TEST_CASE(
234+
"Use pipe to solve a simple SMT problem and get the model, can_receive(-1)",
235+
"[core][util][piped_process]")
236+
{
237+
const std::string binary = "z3 -in -smt2";
238+
const std::string termination_statement = "(exit)\n";
239+
piped_processt process = piped_processt(binary);
240+
241+
piped_processt::process_send_responset res = process.send(
242+
"(set-logic QF_LIA) (declare-const x Int) (declare-const y Int) (assert (> "
243+
"(+ (mod x 4) (* 3 (div y 2))) (- x y))) (check-sat)\n");
244+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
245+
246+
process.can_receive(-1);
247+
std::string response = process.receive();
248+
response.erase(
249+
std::remove(response.begin(), response.end(), '\n'), response.end());
250+
REQUIRE(response == std::string("sat"));
251+
252+
res = process.send(termination_statement);
253+
REQUIRE(res == piped_processt::process_send_responset::SUCCEEDED);
254+
}
255+
256+
#endif

0 commit comments

Comments
 (0)