source: mainline/uspace/lib/pcut/src/os/unix.c@ 1433ecda

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1433ecda was 1433ecda, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix cstyle: make ccheck-fix and commit only files where all the changes are good.

  • Property mode set to 100644
File size: 6.1 KB
Line 
1/*
2 * Copyright (c) 2013 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @file
30 *
31 * Unix-specific functions for test execution via the fork() system call.
32 */
33
34/** We need _POSIX_SOURCE because of kill(). */
35#define _POSIX_SOURCE
36/** We need _BSD_SOURCE because of snprintf() when compiling under C89. */
37#define _BSD_SOURCE
38
39/** Newer versions of features.h needs _DEFAULT_SOURCE. */
40#define _DEFAULT_SOURCE
41
42#include <stdlib.h>
43#include <unistd.h>
44#include <sys/types.h>
45#include <signal.h>
46#include <errno.h>
47#include <assert.h>
48#include <sys/wait.h>
49#include <stdio.h>
50#include <string.h>
51#include "../internal.h"
52
53/** Maximum size of stdout we are able to capture. */
54#define OUTPUT_BUFFER_SIZE 8192
55
56/** Buffer for assertion and other error messages. */
57static char error_message_buffer[OUTPUT_BUFFER_SIZE];
58
59/** Buffer for stdout from the test. */
60static char extra_output_buffer[OUTPUT_BUFFER_SIZE];
61
62/** Prepare for a new test.
63 *
64 * @param test Test that is about to be run.
65 */
66static void before_test_start(pcut_item_t *test)
67{
68 pcut_report_test_start(test);
69
70 memset(error_message_buffer, 0, OUTPUT_BUFFER_SIZE);
71 memset(extra_output_buffer, 0, OUTPUT_BUFFER_SIZE);
72}
73
74/** PID of the forked process running the actual test. */
75static pid_t child_pid;
76
77/** Signal handler that kills the child.
78 *
79 * @param sig Signal number.
80 */
81static void kill_child_on_alarm(int sig)
82{
83 PCUT_UNUSED(sig);
84 kill(child_pid, SIGKILL);
85}
86
87/** Read full buffer from given file descriptor.
88 *
89 * This function exists to overcome the possibility that read() may
90 * not fill the full length of the provided buffer even when EOF is
91 * not reached.
92 *
93 * @param fd Opened file descriptor.
94 * @param buffer Buffer to store data into.
95 * @param buffer_size Size of the @p buffer in bytes.
96 * @return Number of actually read bytes.
97 */
98static size_t read_all(int fd, char *buffer, size_t buffer_size)
99{
100 ssize_t actually_read;
101 char *buffer_start = buffer;
102 do {
103 actually_read = read(fd, buffer, buffer_size);
104 if (actually_read > 0) {
105 buffer += actually_read;
106 buffer_size -= actually_read;
107 if (buffer_size == 0) {
108 break;
109 }
110 }
111 } while (actually_read > 0);
112 if (buffer_start != buffer) {
113 if (*(buffer - 1) == 10) {
114 *(buffer - 1) = 0;
115 buffer--;
116 }
117 }
118 return buffer - buffer_start;
119}
120
121/** Convert program exit code to test outcome.
122 *
123 * @param status Status value from the wait() function.
124 * @return Test outcome code.
125 */
126static int convert_wait_status_to_outcome(int status)
127{
128 if (WIFEXITED(status)) {
129 if (WEXITSTATUS(status) != 0) {
130 return PCUT_OUTCOME_FAIL;
131 } else {
132 return PCUT_OUTCOME_PASS;
133 }
134 }
135
136 if (WIFSIGNALED(status)) {
137 return PCUT_OUTCOME_INTERNAL_ERROR;
138 }
139
140 return status;
141}
142
143/** Run the test in a forked environment and report the result.
144 *
145 * @param self_path Ignored.
146 * @param test Test to be run.
147 */
148int pcut_run_test_forking(const char *self_path, pcut_item_t *test)
149{
150 int link_stdout[2], link_stderr[2];
151 int rc, status, outcome;
152 size_t stderr_size;
153
154 PCUT_UNUSED(self_path);
155
156 before_test_start(test);
157
158
159 rc = pipe(link_stdout);
160 if (rc == -1) {
161 snprintf(error_message_buffer, OUTPUT_BUFFER_SIZE - 1,
162 "pipe() failed: %s.", strerror(rc));
163 pcut_report_test_done(test, PCUT_OUTCOME_INTERNAL_ERROR, error_message_buffer, NULL, NULL);
164 return PCUT_OUTCOME_INTERNAL_ERROR;
165 }
166 rc = pipe(link_stderr);
167 if (rc == -1) {
168 snprintf(error_message_buffer, OUTPUT_BUFFER_SIZE - 1,
169 "pipe() failed: %s.", strerror(rc));
170 pcut_report_test_done(test, PCUT_OUTCOME_INTERNAL_ERROR, error_message_buffer, NULL, NULL);
171 return PCUT_OUTCOME_INTERNAL_ERROR;
172 }
173
174 child_pid = fork();
175 if (child_pid == (pid_t)-1) {
176 snprintf(error_message_buffer, OUTPUT_BUFFER_SIZE - 1,
177 "fork() failed: %s.", strerror(rc));
178 outcome = PCUT_OUTCOME_INTERNAL_ERROR;
179 goto leave_close_pipes;
180 }
181
182 if (child_pid == 0) {
183 /* We are the child. */
184 dup2(link_stdout[1], STDOUT_FILENO);
185 close(link_stdout[0]);
186 dup2(link_stderr[1], STDERR_FILENO);
187 close(link_stderr[0]);
188
189 outcome = pcut_run_test_forked(test);
190
191 exit(outcome);
192 }
193
194 close(link_stdout[1]);
195 close(link_stderr[1]);
196
197 signal(SIGALRM, kill_child_on_alarm);
198 alarm(pcut_get_test_timeout(test));
199
200 stderr_size = read_all(link_stderr[0], extra_output_buffer, OUTPUT_BUFFER_SIZE - 1);
201 read_all(link_stdout[0], extra_output_buffer, OUTPUT_BUFFER_SIZE - 1 - stderr_size);
202
203 wait(&status);
204 alarm(0);
205
206 outcome = convert_wait_status_to_outcome(status);
207
208 goto leave_close_parent_pipe;
209
210leave_close_pipes:
211 close(link_stdout[1]);
212 close(link_stderr[1]);
213leave_close_parent_pipe:
214 close(link_stdout[0]);
215 close(link_stderr[0]);
216
217 pcut_report_test_done_unparsed(test, outcome, extra_output_buffer, OUTPUT_BUFFER_SIZE);
218
219 return outcome;
220}
221
222void pcut_hook_before_test(pcut_item_t *test)
223{
224 PCUT_UNUSED(test);
225
226 /* Do nothing. */
227}
228
Note: See TracBrowser for help on using the repository browser.