source: mainline/uspace/lib/pcut/src/os/unix.c@ 15d0046

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 15d0046 was 134ac5d, checked in by Vojtech Horky <vojtechhorky@…>, 11 years ago

Update PCUT to newest version

  • Property mode set to 100644
File size: 5.7 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 _POSX_SOURCE because of kill(). */
35#define _POSIX_SOURCE
36#include <stdlib.h>
37#include <unistd.h>
38#include <sys/types.h>
39#include <signal.h>
40#include <errno.h>
41#include <assert.h>
42#include <sys/wait.h>
43#include <stdio.h>
44#include <string.h>
45#include "../internal.h"
46
47/** Maximum size of stdout we are able to capture. */
48#define OUTPUT_BUFFER_SIZE 8192
49
50/** Buffer for assertion and other error messages. */
51static char error_message_buffer[OUTPUT_BUFFER_SIZE];
52
53/** Buffer for stdout from the test. */
54static char extra_output_buffer[OUTPUT_BUFFER_SIZE];
55
56/** Prepare for a new test.
57 *
58 * @param test Test that is about to be run.
59 */
60static void before_test_start(pcut_item_t *test) {
61 pcut_report_test_start(test);
62
63 memset(error_message_buffer, 0, OUTPUT_BUFFER_SIZE);
64 memset(extra_output_buffer, 0, OUTPUT_BUFFER_SIZE);
65}
66
67/** PID of the forked process running the actual test. */
68static pid_t child_pid;
69
70/** Signal handler that kills the child.
71 *
72 * @param sig Signal number.
73 */
74static void kill_child_on_alarm(int sig) {
75 PCUT_UNUSED(sig);
76 kill(child_pid, SIGKILL);
77}
78
79/** Read full buffer from given file descriptor.
80 *
81 * This function exists to overcome the possibility that read() may
82 * not fill the full length of the provided buffer even when EOF is
83 * not reached.
84 *
85 * @param fd Opened file descriptor.
86 * @param buffer Buffer to store data into.
87 * @param buffer_size Size of the @p buffer in bytes.
88 * @return Number of actually read bytes.
89 */
90static size_t read_all(int fd, char *buffer, size_t buffer_size) {
91 ssize_t actually_read;
92 char *buffer_start = buffer;
93 do {
94 actually_read = read(fd, buffer, buffer_size);
95 if (actually_read > 0) {
96 buffer += actually_read;
97 buffer_size -= actually_read;
98 if (buffer_size == 0) {
99 break;
100 }
101 }
102 } while (actually_read > 0);
103 if (buffer_start != buffer) {
104 if (*(buffer - 1) == 10) {
105 *(buffer - 1) = 0;
106 buffer--;
107 }
108 }
109 return buffer - buffer_start;
110}
111
112/** Convert program exit code to test outcome.
113 *
114 * @param status Status value from the wait() function.
115 * @return Test outcome code.
116 */
117static int convert_wait_status_to_outcome(int status) {
118 if (WIFEXITED(status)) {
119 if (WEXITSTATUS(status) != 0) {
120 return TEST_OUTCOME_FAIL;
121 } else {
122 return TEST_OUTCOME_PASS;
123 }
124 }
125
126 if (WIFSIGNALED(status)) {
127 return TEST_OUTCOME_ERROR;
128 }
129
130 return status;
131}
132
133/** Run the test in a forked environment and report the result.
134 *
135 * @param self_path Ignored.
136 * @param test Test to be run.
137 */
138void pcut_run_test_forking(const char *self_path, pcut_item_t *test) {
139 PCUT_UNUSED(self_path);
140
141 before_test_start(test);
142
143 int link_stdout[2], link_stderr[2];
144
145 int rc = pipe(link_stdout);
146 if (rc == -1) {
147 snprintf(error_message_buffer, OUTPUT_BUFFER_SIZE - 1,
148 "pipe() failed: %s.", strerror(rc));
149 pcut_report_test_done(test, TEST_OUTCOME_ERROR, error_message_buffer, NULL, NULL);
150 return;
151 }
152 rc = pipe(link_stderr);
153 if (rc == -1) {
154 snprintf(error_message_buffer, OUTPUT_BUFFER_SIZE - 1,
155 "pipe() failed: %s.", strerror(rc));
156 pcut_report_test_done(test, TEST_OUTCOME_ERROR, error_message_buffer, NULL, NULL);
157 return;
158 }
159
160 child_pid = fork();
161 if (child_pid == (pid_t)-1) {
162 snprintf(error_message_buffer, OUTPUT_BUFFER_SIZE - 1,
163 "fork() failed: %s.", strerror(rc));
164 rc = TEST_OUTCOME_ERROR;
165 goto leave_close_pipes;
166 }
167
168 if (child_pid == 0) {
169 /* We are the child. */
170 dup2(link_stdout[1], STDOUT_FILENO);
171 close(link_stdout[0]);
172 dup2(link_stderr[1], STDERR_FILENO);
173 close(link_stderr[0]);
174
175 rc = pcut_run_test_forked(test);
176
177 exit(rc);
178 }
179
180 close(link_stdout[1]);
181 close(link_stderr[1]);
182
183 signal(SIGALRM, kill_child_on_alarm);
184 alarm(pcut_get_test_timeout(test));
185
186 size_t stderr_size = read_all(link_stderr[0], extra_output_buffer, OUTPUT_BUFFER_SIZE - 1);
187 read_all(link_stdout[0], extra_output_buffer, OUTPUT_BUFFER_SIZE - 1 - stderr_size);
188
189 int status;
190 wait(&status);
191 alarm(0);
192
193 rc = convert_wait_status_to_outcome(status);
194
195 goto leave_close_parent_pipe;
196
197leave_close_pipes:
198 close(link_stdout[1]);
199 close(link_stderr[1]);
200leave_close_parent_pipe:
201 close(link_stdout[0]);
202 close(link_stderr[0]);
203
204 pcut_report_test_done_unparsed(test, rc, extra_output_buffer, OUTPUT_BUFFER_SIZE);
205}
Note: See TracBrowser for help on using the repository browser.