xref: /freebsd/lib/libc/tests/stdlib/strfmon_test.c (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
1 /*-
2  * Copyright (C) 2018 Conrad Meyer <cem@FreeBSD.org>
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/param.h>
28 
29 #include <locale.h>
30 #include <monetary.h>
31 #include <stdio.h>
32 
33 #include <atf-c.h>
34 
35 ATF_TC_WITHOUT_HEAD(strfmon_locale_thousands);
36 ATF_TC_BODY(strfmon_locale_thousands, tc)
37 {
38 	char actual[40], expected[40];
39 	struct lconv *lc;
40 	const char *ts;
41 	double n;
42 
43 	setlocale(LC_MONETARY, "sv_SE.UTF-8");
44 
45 	lc = localeconv();
46 
47 	ts = lc->mon_thousands_sep;
48 	if (strlen(ts) == 0)
49 		ts = lc->thousands_sep;
50 
51 	if (strlen(ts) < 2)
52 		atf_tc_skip("multi-byte thousands-separator not found");
53 
54 	n = 1234.56;
55 	strfmon(actual, sizeof(actual) - 1, "%i", n);
56 
57 	strcpy(expected, "1");
58 	strlcat(expected, ts, sizeof(expected));
59 	strlcat(expected, "234", sizeof(expected));
60 
61 	/* We're just testing the thousands separator, not all of strfmon. */
62 	actual[strlen(expected)] = '\0';
63 	ATF_CHECK_STREQ(expected, actual);
64 }
65 
66 ATF_TC_WITHOUT_HEAD(strfmon_examples);
67 ATF_TC_BODY(strfmon_examples, tc)
68 {
69 	const struct {
70 		const char *format;
71 		const char *expected;
72 	} tests[] = {
73 	    { "%n", "[$123.45] [-$123.45] [$3,456.78]" },
74 	    { "%11n", "[    $123.45] [   -$123.45] [  $3,456.78]" },
75 	    { "%#5n", "[ $   123.45] [-$   123.45] [ $ 3,456.78]" },
76 	    { "%=*#5n", "[ $***123.45] [-$***123.45] [ $*3,456.78]" },
77 	    { "%=0#5n", "[ $000123.45] [-$000123.45] [ $03,456.78]" },
78 	    { "%^#5n", "[ $  123.45] [-$  123.45] [ $ 3456.78]" },
79 	    { "%^#5.0n", "[ $  123] [-$  123] [ $ 3457]" },
80 	    { "%^#5.4n", "[ $  123.4500] [-$  123.4500] [ $ 3456.7810]" },
81 	    { "%(#5n", "[ $   123.45 ] [($   123.45)] [ $ 3,456.78 ]" },
82 	    { "%!(#5n", "[    123.45 ] [(   123.45)] [  3,456.78 ]" },
83 	    { "%-14#5.4n", "[ $   123.4500 ] [-$   123.4500 ] [ $ 3,456.7810 ]" },
84 	    { "%14#5.4n", "[  $   123.4500] [ -$   123.4500] [  $ 3,456.7810]" },
85 	};
86 	size_t i;
87 	char actual[100], format[50];
88 
89 	if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
90 		atf_tc_skip("unable to setlocale()");
91 
92 	for (i = 0; i < nitems(tests); ++i) {
93 		snprintf(format, sizeof(format), "[%s] [%s] [%s]",
94 		    tests[i].format, tests[i].format, tests[i].format);
95 		strfmon(actual, sizeof(actual) - 1, format,
96 		    123.45, -123.45, 3456.781);
97 		ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
98 		    "[%s]", tests[i].format);
99 	}
100 }
101 
102 ATF_TC(strfmon_cs_precedes_0);
103 ATF_TC_HEAD(strfmon_cs_precedes_0, tc)
104 {
105 	atf_tc_set_md_var(tc, "descr",
106 	    "sep_by_space x sign_posn when cs_precedes = 0");
107 }
108 ATF_TC_BODY(strfmon_cs_precedes_0, tc)
109 {
110 	const struct {
111 		const char *expected;
112 	} tests[] = {
113 	    /* sep_by_space x sign_posn */
114 	    { "[(123.00$)] [-123.00$] [123.00$-] [123.00-$] [123.00$-]" },
115 	    { "[(123.00 $)] [-123.00 $] [123.00 $-] [123.00 -$] [123.00 $-]" },
116 	    { "[(123.00$)] [- 123.00$] [123.00$ -] [123.00- $] [123.00$ -]" },
117 	};
118 	size_t i, j;
119 	struct lconv *lc;
120 	char actual[100], buf[100];
121 
122 	if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
123 		atf_tc_skip("unable to setlocale()");
124 
125 	lc = localeconv();
126 	lc->n_cs_precedes = 0;
127 
128 	for (i = 0; i < nitems(tests); ++i) {
129 		actual[0] = '\0';
130 		lc->n_sep_by_space = i;
131 
132 		for (j = 0; j < 5; ++j) {
133 			lc->n_sign_posn = j;
134 
135 			strfmon(buf, sizeof(buf) - 1, "[%n] ", -123.0);
136 			strlcat(actual, buf, sizeof(actual));
137 		}
138 
139 		actual[strlen(actual) - 1] = '\0';
140 		ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
141 		    "sep_by_space = %zu", i);
142 	}
143 }
144 
145 ATF_TC(strfmon_cs_precedes_1);
146 ATF_TC_HEAD(strfmon_cs_precedes_1, tc)
147 {
148 	atf_tc_set_md_var(tc, "descr",
149 	    "sep_by_space x sign_posn when cs_precedes = 1");
150 }
151 ATF_TC_BODY(strfmon_cs_precedes_1, tc)
152 {
153 	const struct {
154 		const char *expected;
155 	} tests[] = {
156 	    /* sep_by_space x sign_posn */
157 	    { "[($123.00)] [-$123.00] [$123.00-] [-$123.00] [$-123.00]" },
158 	    { "[($ 123.00)] [-$ 123.00] [$ 123.00-] [-$ 123.00] [$- 123.00]" },
159 	    { "[($123.00)] [- $123.00] [$123.00 -] [- $123.00] [$ -123.00]" },
160 	};
161 	size_t i, j;
162 	struct lconv *lc;
163 	char actual[100], buf[100];
164 
165 	if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
166 		atf_tc_skip("unable to setlocale()");
167 
168 	lc = localeconv();
169 	lc->n_cs_precedes = 1;
170 
171 	for (i = 0; i < nitems(tests); ++i) {
172 		actual[0] = '\0';
173 		lc->n_sep_by_space = i;
174 
175 		for (j = 0; j < 5; ++j) {
176 			lc->n_sign_posn = j;
177 
178 			strfmon(buf, sizeof(buf) - 1, "[%n] ", -123.0);
179 			strlcat(actual, buf, sizeof(actual));
180 		}
181 
182 		actual[strlen(actual) - 1] = '\0';
183 		ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
184 		    "sep_by_space = %zu", i);
185 	}
186 }
187 
188 ATF_TC_WITHOUT_HEAD(strfmon_international_currency_code);
189 ATF_TC_BODY(strfmon_international_currency_code, tc)
190 {
191 	const struct {
192 		const char *locale;
193 		const char *expected;
194 	} tests[] = {
195 	    { "en_US.UTF-8", "[USD123.45]" },
196 	    { "de_DE.UTF-8", "[123,45 EUR]" },
197 	    { "C", "[123.45]" },
198 	};
199 	size_t i;
200 	char actual[100];
201 
202 	for (i = 0; i < nitems(tests); ++i) {
203 		if (setlocale(LC_MONETARY, tests[i].locale) == NULL)
204 			atf_tc_skip("unable to setlocale()");
205 
206 		strfmon(actual, sizeof(actual) - 1, "[%i]", 123.45);
207 		ATF_CHECK_STREQ(tests[i].expected, actual);
208 	}
209 }
210 
211 ATF_TC(strfmon_l);
212 ATF_TC_HEAD(strfmon_l, tc)
213 {
214 	atf_tc_set_md_var(tc, "descr",
215 	    "checks strfmon_l under different locales");
216 }
217 ATF_TC_BODY(strfmon_l, tc)
218 {
219 	const struct {
220 		const char *locale;
221 		const char *expected;
222 	} tests[] = {
223 	    { "C", "[ **1234.57 ] [ **1234.57 ]" },
224 	    { "de_DE.UTF-8", "[ **1234,57 €] [ **1.234,57 EUR]" },
225 	    { "en_GB.UTF-8", "[ £**1234.57] [ GBP**1,234.57]" },
226 	};
227 	locale_t loc;
228 	size_t i;
229 	char buf[100];
230 
231 	for (i = 0; i < nitems(tests); ++i) {
232 		loc = newlocale(LC_MONETARY_MASK, tests[i].locale, NULL);
233 		ATF_REQUIRE(loc != NULL);
234 
235 		strfmon_l(buf, sizeof(buf) - 1, loc, "[%^=*#6n] [%=*#6i]",
236 		    1234.567, 1234.567);
237 		ATF_REQUIRE_STREQ(tests[i].expected, buf);
238 
239 		freelocale(loc);
240 	}
241 }
242 
243 ATF_TP_ADD_TCS(tp)
244 {
245 	ATF_TP_ADD_TC(tp, strfmon_locale_thousands);
246 	ATF_TP_ADD_TC(tp, strfmon_examples);
247 	ATF_TP_ADD_TC(tp, strfmon_cs_precedes_0);
248 	ATF_TP_ADD_TC(tp, strfmon_cs_precedes_1);
249 	ATF_TP_ADD_TC(tp, strfmon_international_currency_code);
250 	ATF_TP_ADD_TC(tp, strfmon_l);
251 	return (atf_no_error());
252 }
253