1/*-
2 * Copyright (c) 2012 Jilles Tjoelker
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 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/wait.h>
32#include <err.h>
33#include <errno.h>
34#include <fmtmsg.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39
40#include <atf-c.h>
41
42static char *run_test(long classification, const char *label, int severity,
43    const char *text, const char *action, const char *tag);
44
45struct testcase {
46	long classification;
47	const char *label;
48	int severity;
49	const char *text;
50	const char *action;
51	const char *tag;
52	const char *msgverb;
53	const char *result;
54} testcases[] = {
55	{
56		MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
57		"illegal option -- z", "refer to manual", "BSD:ls:001",
58		NULL,
59		"BSD:ls: ERROR: illegal option -- z\n"
60		    "TO FIX: refer to manual BSD:ls:001\n"
61	},
62	{
63		MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
64		"illegal option -- z", "refer to manual", "BSD:ls:001",
65		"text:severity:action:tag",
66		"illegal option -- z: ERROR\n"
67		    "TO FIX: refer to manual BSD:ls:001\n"
68	},
69	{
70		MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
71		"illegal option -- z", "refer to manual", "BSD:ls:001",
72		"text",
73		"illegal option -- z\n"
74	},
75	{
76		MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
77		"illegal option -- z", "refer to manual", "BSD:ls:001",
78		"severity:text",
79		"ERROR: illegal option -- z\n"
80	},
81	{
82		MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
83		"illegal option -- z", "refer to manual", "BSD:ls:001",
84		"ignore me",
85		"BSD:ls: ERROR: illegal option -- z\n"
86		    "TO FIX: refer to manual BSD:ls:001\n"
87	},
88	{
89		MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
90		"illegal option -- z", "refer to manual", "BSD:ls:001",
91		"tag:severity:text:nothing:action",
92		"BSD:ls: ERROR: illegal option -- z\n"
93		    "TO FIX: refer to manual BSD:ls:001\n"
94	},
95	{
96		MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
97		"illegal option -- z", "refer to manual", "BSD:ls:001",
98		"",
99		"BSD:ls: ERROR: illegal option -- z\n"
100		    "TO FIX: refer to manual BSD:ls:001\n"
101	},
102	{
103		MM_UTIL | MM_PRINT, MM_NULLLBL, MM_ERROR,
104		"illegal option -- z", "refer to manual", "BSD:ls:001",
105		NULL,
106		"ERROR: illegal option -- z\n"
107		    "TO FIX: refer to manual BSD:ls:001\n"
108	},
109	{
110		MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
111		"illegal option -- z", MM_NULLACT, MM_NULLTAG,
112		NULL,
113		"BSD:ls: ERROR: illegal option -- z\n"
114	},
115	{
116		MM_UTIL | MM_NULLMC, "BSD:ls", MM_ERROR,
117		"illegal option -- z", "refer to manual", "BSD:ls:001",
118		NULL,
119		""
120	},
121	{
122		MM_APPL | MM_PRINT, "ABCDEFGHIJ:abcdefghijklmn", MM_INFO,
123		"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
124		    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
125		    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
126		    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
127		"refer to manual", "ABCDEFGHIJ:abcdefghijklmn:001",
128		NULL,
129		"ABCDEFGHIJ:abcdefghijklmn: INFO: "
130		    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
131		    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
132		    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
133		    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"
134		    "TO FIX: refer to manual ABCDEFGHIJ:abcdefghijklmn:001\n"
135	},
136	{
137		MM_OPSYS | MM_PRINT, "TEST:test", MM_HALT,
138		"failed", "nothing can help me", "NOTHING",
139		NULL,
140		"TEST:test: HALT: failed\n"
141		    "TO FIX: nothing can help me NOTHING\n"
142	},
143	{
144		MM_OPSYS | MM_PRINT, "TEST:test", MM_WARNING,
145		"failed", "nothing can help me", "NOTHING",
146		NULL,
147		"TEST:test: WARNING: failed\n"
148		    "TO FIX: nothing can help me NOTHING\n"
149	},
150	{
151		MM_OPSYS | MM_PRINT, "TEST:test", MM_NOSEV,
152		"failed", "nothing can help me", "NOTHING",
153		NULL,
154		"TEST:test: failed\n"
155		    "TO FIX: nothing can help me NOTHING\n"
156	}
157};
158
159static char *
160run_test(long classification, const char *label, int severity,
161    const char *text, const char *action, const char *tag)
162{
163	int pip[2];
164	pid_t pid, wpid;
165	char *result, *p;
166	size_t resultsize;
167	ssize_t n;
168	int status;
169
170	if (pipe(pip) == -1)
171		err(2, "pipe");
172	pid = fork();
173	if (pid == -1)
174		err(2, "fork");
175	if (pid == 0) {
176		close(pip[0]);
177		if (pip[1] != STDERR_FILENO &&
178		    dup2(pip[1], STDERR_FILENO) == -1)
179			_exit(2);
180		if (fmtmsg(classification, label, severity, text, action, tag)
181		    != MM_OK)
182			_exit(1);
183		else
184			_exit(0);
185	}
186	close(pip[1]);
187	resultsize = 1024;
188	result = malloc(resultsize);
189	p = result;
190	while ((n = read(pip[0], p, result + resultsize - p - 1)) != 0) {
191		if (n == -1) {
192			if (errno == EINTR)
193				continue;
194			else
195				err(2, "read");
196		}
197		p += n;
198		if (result + resultsize == p - 1) {
199			resultsize *= 2;
200			result = realloc(result, resultsize);
201			if (result == NULL)
202				err(2, "realloc");
203		}
204	}
205	if (memchr(result, '\0', p - result) != NULL) {
206		free(result);
207		return (NULL);
208	}
209	*p = '\0';
210	close(pip[0]);
211	while ((wpid = waitpid(pid, &status, 0)) == -1 && errno == EINTR)
212		;
213	if (wpid == -1)
214		err(2, "waitpid");
215	if (status != 0) {
216		free(result);
217		return (NULL);
218	}
219	return (result);
220}
221
222ATF_TC_WITHOUT_HEAD(fmtmsg_test);
223ATF_TC_BODY(fmtmsg_test, tc)
224{
225	char *result;
226	struct testcase *t;
227	int i;
228
229	for (i = 0; i < nitems(testcases); i++) {
230		t = &testcases[i];
231		if (t->msgverb != NULL)
232			setenv("MSGVERB", t->msgverb, 1);
233		else
234			unsetenv("MSGVERB");
235		result = run_test(t->classification, t->label, t->severity,
236		    t->text, t->action, t->tag);
237		ATF_CHECK_MSG(result != NULL, "testcase %d failed", i + 1);
238		if (result != NULL)
239			ATF_CHECK_MSG(strcmp(result, t->result) == 0,
240			    "results for testcase %d didn't match; "
241			    "`%s` != `%s`", i + 1, result, t->result);
242		free(result);
243	}
244}
245
246ATF_TP_ADD_TCS(tp)
247{
248
249	ATF_TP_ADD_TC(tp, fmtmsg_test);
250
251	return (atf_no_error());
252}
253