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