1 /*
2    Unix SMB/CIFS implementation.
3    Run subunit tests
4    Copyright (C) Jelmer Vernooij 2006
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "includes.h"
22 #include "system/dir.h"
23 #include "system/network.h"
24 #include "system/filesys.h"
25 #include "torture/ui.h"
26 #include "torture/torture.h"
27 #include "torture/proto.h"
28 
torture_subunit_suite_create(TALLOC_CTX * mem_ctx,const char * path)29 struct torture_suite *torture_subunit_suite_create(TALLOC_CTX *mem_ctx,
30 														 const char *path)
31 {
32 	struct torture_suite *suite = talloc_zero(mem_ctx, struct torture_suite);
33 
34 	suite->path = talloc_strdup(suite, path);
35 	suite->name = talloc_strdup(suite, strrchr(path, '/')?strrchr(path, '/')+1:
36 									   path);
37 	suite->description = talloc_asprintf(suite, "Subunit test %s", suite->name);
38 
39 	return suite;
40 }
41 
torture_subunit_load_testsuites(const char * directory,bool recursive,struct torture_suite * parent)42 bool torture_subunit_load_testsuites(const char *directory, bool recursive,
43 									struct torture_suite *parent)
44 {
45 	DIR *dir;
46 	struct dirent *entry;
47 	char *filename;
48 	bool exists;
49 
50 	if (parent == NULL)
51 		parent = torture_root;
52 
53 	dir = opendir(directory);
54 	if (dir == NULL)
55 		return true;
56 
57 	while((entry = readdir(dir))) {
58 		struct torture_suite *child;
59 		if (entry->d_name[0] == '.')
60 			continue;
61 
62 		filename = talloc_asprintf(NULL, "%s/%s", directory, entry->d_name);
63 
64 		if (!recursive && directory_exist(filename)) {
65 			talloc_free(filename);
66 			continue;
67 		}
68 
69 		if (directory_exist(filename)) {
70 			child = torture_find_suite(parent, entry->d_name);
71 			exists = (child != NULL);
72 			if (child == NULL)
73 				child = torture_suite_create(parent, entry->d_name);
74 			torture_subunit_load_testsuites(filename, true, child);
75 		} else {
76 			child = torture_subunit_suite_create(parent, filename);
77 			exists = false;
78 		}
79 
80 		if (!exists) {
81 			torture_suite_add_suite(parent, child);
82 		}
83 
84 		talloc_free(filename);
85 	}
86 
87 	closedir(dir);
88 
89 	return true;
90 }
91 
piped_child(char * const command[],int * f_stdout,int * f_stderr)92 static pid_t piped_child(char* const command[], int *f_stdout, int *f_stderr)
93 {
94 	pid_t pid;
95 	int sock_out[2], sock_err[2];
96 
97 	if (pipe(sock_out) == -1) {
98 		DEBUG(0, ("socketpair: %s", strerror(errno)));
99 		return -1;
100 	}
101 
102 	if (pipe(sock_err) == -1) {
103 		DEBUG(0, ("socketpair: %s", strerror(errno)));
104 		return -1;
105 	}
106 
107 	*f_stdout = sock_out[0];
108 	*f_stderr = sock_err[0];
109 
110 	pid = fork();
111 
112 	if (pid == -1) {
113 		DEBUG(0, ("fork: %s", strerror(errno)));
114 		return -1;
115 	}
116 
117 	if (pid == 0) {
118 		close(0);
119 		close(1);
120 		close(2);
121 		close(sock_out[0]);
122 		close(sock_err[0]);
123 
124 		open("/dev/null", O_RDONLY);
125 		dup2(sock_out[1], 1);
126 		dup2(sock_err[1], 2);
127 		execvp(command[0], command);
128 		exit(-1);
129 	}
130 
131 	close(sock_out[1]);
132 	close(sock_err[1]);
133 
134 	return pid;
135 }
136 
137 enum subunit_field { SUBUNIT_TEST, SUBUNIT_SUCCESS, SUBUNIT_FAILURE,
138 					 SUBUNIT_SKIP };
139 
run_subunit_message(struct torture_context * context,enum subunit_field field,const char * name,const char * comment)140 static void run_subunit_message(struct torture_context *context,
141 								enum subunit_field field,
142 								const char *name,
143 								const char *comment)
144 {
145 	struct torture_test test;
146 
147 	ZERO_STRUCT(test);
148 	test.name = name;
149 
150 	switch (field) {
151 	case SUBUNIT_TEST:
152 		test.description = comment;
153 		torture_ui_test_start(context, NULL, &test);
154 		break;
155 	case SUBUNIT_FAILURE:
156 		context->active_test = &test;
157 		torture_ui_test_result(context, TORTURE_FAIL, comment);
158 		context->active_test = NULL;
159 		break;
160 	case SUBUNIT_SUCCESS:
161 		context->active_test = &test;
162 		torture_ui_test_result(context, TORTURE_OK, comment);
163 		context->active_test = NULL;
164 		break;
165 	case SUBUNIT_SKIP:
166 		context->active_test = &test;
167 		torture_ui_test_result(context, TORTURE_SKIP, comment);
168 		context->active_test = NULL;
169 		break;
170 	}
171 }
172 
torture_subunit_run_suite(struct torture_context * context,struct torture_suite * suite)173 bool torture_subunit_run_suite(struct torture_context *context,
174 					   struct torture_suite *suite)
175 {
176 	static char *command[2];
177 	int fd_out, fd_err;
178 	pid_t pid;
179 	size_t size;
180 	char *p, *q;
181 	char *comment = NULL;
182 	char *name = NULL;
183 	enum subunit_field lastfield;
184 	int status;
185 	char buffer[4096];
186 	size_t offset = 0;
187 
188 
189 	command[0] = talloc_strdup(context, suite->path);
190 	command[1] = NULL;
191 
192 	pid = piped_child(command, &fd_out, &fd_err);
193 	if (pid == -1)
194 		return false;
195 
196 	while (1) {
197 		fd_set fds;
198 		char *eol;
199 
200 		FD_ZERO(&fds);
201 
202 		FD_SET(fd_out, &fds);
203 		FD_SET(fd_err, &fds);
204 
205 		if (select(MAX(fd_out,fd_err)+1, &fds, NULL, NULL, NULL) <= 0) break;
206 
207 		if (FD_ISSET(fd_err, &fds)) {
208 			size = read(fd_err, buffer+offset, sizeof(buffer) - (offset+1));
209 			if (size <= 0) break;
210 			write(2, buffer+offset, size);
211 			continue;
212 		}
213 
214 		if (!FD_ISSET(fd_out, &fds)) continue;
215 
216 		size = read(fd_out, buffer+offset, sizeof(buffer) - (offset+1));
217 
218 		if (size <= 0) break;
219 
220 		buffer[offset+size] = '\0';
221 
222 		write(1, buffer+offset, size);
223 
224 		for (p = buffer; p; p = eol+1) {
225 			eol = strchr(p, '\n');
226 			if (eol == NULL)
227 				break;
228 
229 			*eol = '\0';
230 
231 			if (comment != NULL && strcmp(p, "]") == 0) {
232 				run_subunit_message(context, lastfield, name, comment);
233 				talloc_free(name); name = NULL;
234 				talloc_free(comment); comment = NULL;
235 			} else if (comment != NULL) {
236 				comment = talloc_append_string(context, comment, p);
237 			} else {
238 				q = strchr(p, ':');
239 				if (q == NULL) {
240 					torture_comment(context, "Invalid line `%s'\n", p);
241 					continue;
242 				}
243 
244 				*q = '\0';
245 				if (!strcmp(p, "test")) {
246 					lastfield = SUBUNIT_TEST;
247 				} else if (!strcmp(p, "failure")) {
248 					lastfield = SUBUNIT_FAILURE;
249 				} else if (!strcmp(p, "success")) {
250 					lastfield = SUBUNIT_SUCCESS;
251 				} else if (!strcmp(p, "skip")) {
252 					lastfield = SUBUNIT_SKIP;
253 				} else {
254 					torture_comment(context, "Invalid subunit field `%s'\n", p);
255 					continue;
256 				}
257 
258 				p = q+1;
259 
260 				name = talloc_strdup(context, p+1);
261 
262 				q = strrchr(p, '[');
263 				if (q != NULL) {
264 					*q = '\0';
265 					comment = talloc_strdup(context, "");
266 				} else {
267 					run_subunit_message(context, lastfield, name, NULL);
268 					talloc_free(name);
269 					name = NULL;
270 				}
271 			}
272 		}
273 
274 		offset += size-(p-buffer);
275 		memcpy(buffer, p, offset);
276 	}
277 
278 	if (waitpid(pid, &status, 0) == -1) {
279 		torture_result(context, TORTURE_ERROR, "waitpid(%d) failed\n", pid);
280 		return false;
281 	}
282 
283 	if (WEXITSTATUS(status) != 0) {
284 		torture_result(context, TORTURE_ERROR, "failed with status %d\n", WEXITSTATUS(status));
285 		return false;
286 	}
287 
288 	if (name != NULL) {
289 		torture_result(context, TORTURE_ERROR, "Interrupted during %s\n", name);
290 		return false;
291 	}
292 
293 	return true;
294 }
295