xref: /aosp_15_r20/external/vboot_reference/tests/subprocess_tests.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2019 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include "subprocess.h"
10 #include "common/tests.h"
11 
12 #define TEST_STRING "hello world"
13 #define TEST_STRING_LN TEST_STRING "\n"
14 
test_subprocess_output_to_buffer(void)15 static void test_subprocess_output_to_buffer(void)
16 {
17 	char output_buffer[__builtin_strlen(TEST_STRING_LN)];
18 
19 	struct subprocess_target output = {
20 		.type = TARGET_BUFFER,
21 		.buffer = {
22 			.buf = output_buffer,
23 			.size = sizeof(output_buffer),
24 		},
25 	};
26 	const char *const argv[] = {
27 		"echo", TEST_STRING, NULL
28 	};
29 
30 	TEST_EQ(subprocess_run(argv, &subprocess_null, &output, NULL), 0,
31 		"Return value of \"echo 'hello world'\" is 0");
32 	TEST_EQ(memcmp(output_buffer, TEST_STRING_LN, sizeof(output_buffer)), 0,
33 		"Output is \"hello world\\n\"");
34 	TEST_EQ(output.buffer.bytes_consumed, sizeof(output_buffer),
35 		"The entire output buffer should have been used.");
36 }
37 
test_subprocess_output_to_buffer_null_terminated(void)38 static void test_subprocess_output_to_buffer_null_terminated(void)
39 {
40 	char output_buffer[__builtin_strlen(TEST_STRING_LN) + 1];
41 
42 	struct subprocess_target output = {
43 		.type = TARGET_BUFFER_NULL_TERMINATED,
44 		.buffer = {
45 			.buf = output_buffer,
46 			.size = sizeof(output_buffer),
47 		},
48 	};
49 	const char *const argv[] = {
50 		"echo", TEST_STRING, NULL
51 	};
52 
53 	TEST_EQ(subprocess_run(argv, &subprocess_null, &output, NULL), 0,
54 		"Return value of \"echo 'hello world'\" is 0");
55 	TEST_STR_EQ(output_buffer, TEST_STRING_LN,
56 		    "Output is \"hello world\\n\"");
57 	TEST_EQ(output.buffer.bytes_consumed, sizeof(output_buffer) - 1,
58 		"The entire output buffer should have been used.");
59 }
60 
61 #define TEST_STRING_2 "hello\0world!"
62 
test_subprocess_input_buffer(void)63 static void test_subprocess_input_buffer(void)
64 {
65 	char input_buffer[sizeof(TEST_STRING_2)];
66 	char output_buffer[20];
67 	char error_buffer[20];
68 
69 	memcpy(input_buffer, TEST_STRING_2, sizeof(input_buffer));
70 
71 	struct subprocess_target input = {
72 		.type = TARGET_BUFFER,
73 		.buffer = {
74 			.buf = input_buffer,
75 			.size = sizeof(input_buffer),
76 		},
77 	};
78 	struct subprocess_target output = {
79 		.type = TARGET_BUFFER_NULL_TERMINATED,
80 		.buffer = {
81 			.buf = output_buffer,
82 			.size = sizeof(output_buffer),
83 		},
84 	};
85 	struct subprocess_target error = {
86 		.type = TARGET_BUFFER_NULL_TERMINATED,
87 		.buffer = {
88 			.buf = error_buffer,
89 			.size = sizeof(error_buffer),
90 		},
91 	};
92 	const char *const argv[] = {"cat", NULL};
93 
94 	TEST_EQ(subprocess_run(argv, &input, &output, &error), 0,
95 		"Return value of \"cat\" is 0");
96 	TEST_EQ(memcmp(output_buffer, TEST_STRING_2, sizeof(TEST_STRING_2)),
97 		0, "Output is \"hello\\0world!\"");
98 	TEST_STR_EQ(error_buffer, "", "No output captured on stderr");
99 	TEST_EQ(output.buffer.bytes_consumed, sizeof(TEST_STRING_2),
100 		"Bytes consumed is correct");
101 	TEST_EQ(error.buffer.bytes_consumed, 0, "No bytes used for error");
102 }
103 
test_subprocess_input_null_terminated(void)104 static void test_subprocess_input_null_terminated(void)
105 {
106 	char input_buffer[20];
107 	char output_buffer[20];
108 	char error_buffer[20];
109 
110 	memcpy(input_buffer, TEST_STRING_2, sizeof(TEST_STRING_2));
111 
112 	struct subprocess_target input = {
113 		.type = TARGET_BUFFER_NULL_TERMINATED,
114 		.buffer = {
115 			.buf = input_buffer,
116 		},
117 	};
118 	struct subprocess_target output = {
119 		.type = TARGET_BUFFER_NULL_TERMINATED,
120 		.buffer = {
121 			.buf = output_buffer,
122 			.size = sizeof(output_buffer),
123 		},
124 	};
125 	struct subprocess_target error = {
126 		.type = TARGET_BUFFER_NULL_TERMINATED,
127 		.buffer = {
128 			.buf = error_buffer,
129 			.size = sizeof(error_buffer),
130 		},
131 	};
132 	const char *const argv[] = {"cat", NULL};
133 
134 	TEST_EQ(subprocess_run(argv, &input, &output, &error), 0,
135 		"Return value of \"cat\" is 0");
136 	TEST_STR_EQ(output_buffer, "hello", "Output is \"hello\"");
137 	TEST_STR_EQ(error_buffer, "", "No output captured on stderr");
138 	TEST_EQ(output.buffer.bytes_consumed, 5, "5 bytes used");
139 	TEST_EQ(error.buffer.bytes_consumed, 0, "No bytes used for error");
140 }
141 
test_subprocess_small_output_buffer(void)142 static void test_subprocess_small_output_buffer(void)
143 {
144 	char output_buffer[3];
145 
146 	struct subprocess_target output = {
147 		.type = TARGET_BUFFER_NULL_TERMINATED,
148 		.buffer = {
149 			.buf = output_buffer,
150 			.size = sizeof(output_buffer),
151 		},
152 	};
153 	const char *const argv[] = {
154 		"echo", TEST_STRING, NULL
155 	};
156 
157 	TEST_EQ(subprocess_run(argv, &subprocess_null, &output, NULL), 0,
158 		"Return value of \"echo 'hello world'\" is 0");
159 	TEST_STR_EQ(output_buffer, "he",
160 		    "Output is \"he\" (truncated to small buffer)");
161 	TEST_EQ(output.buffer.bytes_consumed, sizeof(output_buffer) - 1,
162 		"The entire output buffer should have been used.");
163 }
164 
test_subprocess_return_code_failure(void)165 static void test_subprocess_return_code_failure(void)
166 {
167 	const char *const argv[] = {"false", NULL};
168 
169 	TEST_NEQ(subprocess_run(argv, NULL, NULL, NULL), 0,
170 		 "Return value of \"false\" is nonzero");
171 }
172 
173 struct cb_ctx {
174 	char buffer[49 * 1024];
175 	char *ptr;
176 };
177 
input_cb(char * buf,size_t buf_sz,void * data)178 static ssize_t input_cb(char *buf, size_t buf_sz, void *data)
179 {
180 	struct cb_ctx *ctx = (struct cb_ctx *)data;
181 	size_t len = (ctx->buffer + sizeof(ctx->buffer)) - ctx->ptr;
182 	if (len > buf_sz)
183 		len = buf_sz;
184 	memcpy(buf, ctx->ptr, len);
185 	ctx->ptr += len;
186 	return len;
187 }
188 
test_subprocess_input_from_cb(void)189 static void test_subprocess_input_from_cb(void)
190 {
191 	struct cb_ctx ctx;
192 	char output_buffer[sizeof(ctx.buffer)];
193 	const char *const argv[] = {"cat", NULL};
194 
195 	/* Initialize the input buffer with some data */
196 	for (size_t i = 0; i < sizeof(ctx.buffer); i++)
197 		ctx.buffer[i] = (char)i;
198 	ctx.ptr = ctx.buffer;
199 
200 	struct subprocess_target output = {
201 		.type = TARGET_BUFFER,
202 		.buffer = {
203 			.buf = output_buffer,
204 			.size = sizeof(output_buffer),
205 		},
206 	};
207 
208 	struct subprocess_target input = {
209 		.type = TARGET_CALLBACK,
210 		.callback = {
211 			.cb = input_cb,
212 			.data = &ctx,
213 		},
214 	};
215 	TEST_EQ(subprocess_run(argv, &input, &output, NULL), 0,
216 		"Return value of \"cat\" is zero.");
217 	TEST_EQ(memcmp(ctx.buffer, output_buffer, sizeof(output_buffer)), 0,
218 		"The input buffer is equal to the output buffer.");
219 	TEST_EQ(output.buffer.bytes_consumed, sizeof(output_buffer),
220 		"The entire output buffer should have been used.");
221 }
222 
output_cb(char * buf,size_t buf_sz,void * data)223 static ssize_t output_cb(char *buf, size_t buf_sz, void *data)
224 {
225 	struct cb_ctx *ctx = (struct cb_ctx *)data;
226 	if (ctx->ptr + buf_sz > ctx->buffer + sizeof(ctx->buffer)) {
227 		TEST_TRUE(0, "Test failed as there is not enough space in the "
228 			  "output buffer.");
229 		return -1;
230 	}
231 	memcpy(ctx->ptr, buf, buf_sz);
232 	ctx->ptr += buf_sz;
233 	return 0;
234 }
235 
test_subprocess_output_to_cb(void)236 static void test_subprocess_output_to_cb(void)
237 {
238 	struct cb_ctx ctx;
239 	char output_buffer[sizeof(ctx.buffer)];
240 	const char *const argv[] = {
241 		"bc", "-l", NULL
242 	};
243 
244 	ctx.ptr = ctx.buffer;
245 
246 	struct subprocess_target input = {
247 		.type = TARGET_BUFFER_NULL_TERMINATED,
248 		.buffer = {
249 			.buf = (char *)"for (i = 0; i <= 10000; i += 1) i\n",
250 		},
251 	};
252 
253 	struct subprocess_target target_via_buffer = {
254 		.type = TARGET_BUFFER,
255 		.buffer = {
256 			.buf = output_buffer,
257 			.size = sizeof(output_buffer),
258 		},
259 	};
260 
261 	struct subprocess_target target_via_cb = {
262 		.type = TARGET_CALLBACK,
263 		.callback = {
264 			.cb = output_cb,
265 			.data = &ctx,
266 		},
267 	};
268 
269 	TEST_EQ(subprocess_run(argv, &input, &target_via_buffer, NULL), 0,
270 		"Return value is zero when using buffer.");
271 	TEST_EQ(subprocess_run(argv, &input, &target_via_cb, NULL), 0,
272 		"Return value is zero when using callback.");
273 	TEST_EQ(ctx.ptr - ctx.buffer, target_via_buffer.buffer.bytes_consumed,
274 		"Both commmand invocations used the same number of bytes.");
275 	TEST_EQ(memcmp(output_buffer, ctx.buffer,
276 		       target_via_buffer.buffer.bytes_consumed),
277 		0, "Both output buffers are equivalent.");
278 }
279 
main(int argc,char * argv[])280 int main(int argc, char *argv[])
281 {
282 	test_subprocess_output_to_buffer();
283 	test_subprocess_output_to_buffer_null_terminated();
284 	test_subprocess_input_buffer();
285 	test_subprocess_input_null_terminated();
286 	test_subprocess_small_output_buffer();
287 	test_subprocess_return_code_failure();
288 	test_subprocess_input_from_cb();
289 	test_subprocess_output_to_cb();
290 
291 	return gTestSuccess ? 0 : 255;
292 }
293