Home | History | Annotate | Line # | Download | only in locale
t_strfmon.c revision 1.6
      1 /* $NetBSD: t_strfmon.c,v 1.6 2023/11/27 19:45:36 christos Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2017 The NetBSD Foundation, Inc.
      5  * Copyright (C) 2018 Conrad Meyer <cem (at) FreeBSD.org>
      6  * All rights reserved.
      7  *
      8  * This code is derived from software contributed to The NetBSD Foundation
      9  * by Joerg Sonnenberger.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 __RCSID("$NetBSD: t_strfmon.c,v 1.6 2023/11/27 19:45:36 christos Exp $");
     35 
     36 #include <atf-c.h>
     37 #include <stdio.h>
     38 #include <locale.h>
     39 #include <monetary.h>
     40 
     41 ATF_TC(strfmon_locale);
     42 
     43 ATF_TC_HEAD(strfmon_locale, tc)
     44 {
     45 	atf_tc_set_md_var(tc, "descr",
     46 		"Checks strfmon_l under different locales");
     47 }
     48 
     49 ATF_TC_BODY(strfmon_locale, tc)
     50 {
     51 	const struct {
     52 		const char *locale;
     53 		const char *expected;
     54 	} tests[] = {
     55 	    { "C", "[ **1234.57 ] [ **1234.57 ]" },
     56 	    { "de_DE.UTF-8", "[ **1234,57 ] [ **1.234,57 EUR]" },
     57 	    { "en_GB.UTF-8", "[ **1234.57] [ GBP**1,234.57]" },
     58 	};
     59 	locale_t loc;
     60 	size_t i;
     61 	char buf[80];
     62 	for (i = 0; i < __arraycount(tests); ++i) {
     63 		loc = newlocale(LC_MONETARY_MASK, tests[i].locale, 0);
     64 		ATF_REQUIRE(loc != 0);
     65 		strfmon_l(buf, sizeof(buf), loc, "[%^=*#6n] [%=*#6i]",
     66 		    1234.567, 1234.567);
     67 		ATF_REQUIRE_STREQ(tests[i].expected, buf);
     68 		freelocale(loc);
     69 	}
     70 }
     71 
     72 ATF_TC(strfmon_pad);
     73 
     74 ATF_TC_HEAD(strfmon_pad, tc)
     75 {
     76 	atf_tc_set_md_var(tc, "descr", "Checks strfmon padding");
     77 }
     78 
     79 ATF_TC_BODY(strfmon_pad, tc)
     80 {
     81     char string[1024];
     82 
     83     ATF_REQUIRE(setlocale(LC_MONETARY, "en_US.UTF-8") != NULL);
     84     strfmon(string, sizeof(string), "[%8n] [%8n]", 123.45, 123.45);
     85     ATF_REQUIRE_STREQ(string, "[ $123.45] [ $123.45]");
     86 }
     87 
     88 ATF_TC(strfmon_locale_thousands);
     89 
     90 ATF_TC_HEAD(strfmon_locale_thousands, tc)
     91 {
     92 	atf_tc_set_md_var(tc, "descr",
     93 	    "Checks strfmon locale thousands separator");
     94 }
     95 
     96 ATF_TC_BODY(strfmon_locale_thousands, tc)
     97 {
     98 	char actual[40], expected[40];
     99 	struct lconv *lc;
    100 	const char *ts;
    101 	double n;
    102 
    103 	setlocale(LC_MONETARY, "sv_SE.UTF-8");
    104 
    105 	lc = localeconv();
    106 
    107 	ts = lc->mon_thousands_sep;
    108 	if (strlen(ts) == 0)
    109 		ts = lc->thousands_sep;
    110 
    111 	if (strlen(ts) < 2)
    112 		atf_tc_skip("multi-byte thousands-separator not found");
    113 
    114 	n = 1234.56;
    115 	strfmon(actual, sizeof(actual) - 1, "%i", n);
    116 
    117 	strcpy(expected, "1");
    118 	strlcat(expected, ts, sizeof(expected));
    119 	strlcat(expected, "234", sizeof(expected));
    120 
    121 	/* We're just testing the thousands separator, not all of strfmon. */
    122 	actual[strlen(expected)] = '\0';
    123 	ATF_CHECK_STREQ(expected, actual);
    124 }
    125 
    126 ATF_TC(strfmon_examples);
    127 ATF_TC_HEAD(strfmon_examples, tc) {
    128 	atf_tc_set_md_var(tc, "descr",
    129 	    "Checks strfmon field formats");
    130 }
    131 
    132 ATF_TC_BODY(strfmon_examples, tc)
    133 {
    134 	const struct {
    135 		const char *format;
    136 		const char *expected;
    137 	} tests[] = {
    138 	    { "%n", "[$123.45] [-$123.45] [$3,456.78]" },
    139 	    { "%11n", "[    $123.45] [   -$123.45] [  $3,456.78]" },
    140 	    { "%#5n", "[ $   123.45] [-$   123.45] [ $ 3,456.78]" },
    141 	    { "%=*#5n", "[ $***123.45] [-$***123.45] [ $*3,456.78]" },
    142 	    { "%=0#5n", "[ $000123.45] [-$000123.45] [ $03,456.78]" },
    143 	    { "%^#5n", "[ $  123.45] [-$  123.45] [ $ 3456.78]" },
    144 	    { "%^#5.0n", "[ $  123] [-$  123] [ $ 3457]" },
    145 	    { "%^#5.4n", "[ $  123.4500] [-$  123.4500] [ $ 3456.7810]" },
    146 	    { "%(#5n", "[ $   123.45 ] [($   123.45)] [ $ 3,456.78 ]" },
    147 	    { "%!(#5n", "[    123.45 ] [(   123.45)] [  3,456.78 ]" },
    148 	    { "%-14#5.4n", "[ $   123.4500 ] [-$   123.4500 ] [ $ 3,456.7810 ]" },
    149 	    { "%14#5.4n", "[  $   123.4500] [ -$   123.4500] [  $ 3,456.7810]" },
    150 	};
    151 	size_t i;
    152 	char actual[100], format[50];
    153 
    154 	if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
    155 		atf_tc_skip("unable to setlocale()");
    156 
    157 	for (i = 0; i < __arraycount(tests); ++i) {
    158 		snprintf(format, sizeof(format), "[%s] [%s] [%s]",
    159 		    tests[i].format, tests[i].format, tests[i].format);
    160 		strfmon(actual, sizeof(actual) - 1, format,
    161 		    123.45, -123.45, 3456.781);
    162 		ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
    163 		    "[%s]", tests[i].format);
    164 	}
    165 }
    166 
    167 ATF_TC(strfmon_cs_precedes_0);
    168 
    169 ATF_TC_HEAD(strfmon_cs_precedes_0, tc)
    170 {
    171 	atf_tc_set_md_var(tc, "descr",
    172 	    "sep_by_space x sign_posn when cs_precedes = 0");
    173 }
    174 
    175 ATF_TC_BODY(strfmon_cs_precedes_0, tc)
    176 {
    177 	const struct {
    178 		const char *expected;
    179 	} tests[] = {
    180 	    /* sep_by_space x sign_posn */
    181 	    { "[(123.00$)] [-123.00$] [123.00$-] [123.00-$] [123.00$-]" },
    182 	    { "[(123.00 $)] [-123.00 $] [123.00 $-] [123.00 -$] [123.00 $-]" },
    183 	    { "[(123.00$)] [- 123.00$] [123.00$ -] [123.00- $] [123.00$ -]" },
    184 	};
    185 	size_t i, j;
    186 	struct lconv *lc;
    187 	char actual[100], buf[100];
    188 
    189 	if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
    190 		atf_tc_skip("unable to setlocale()");
    191 
    192 	lc = localeconv();
    193 	lc->n_cs_precedes = 0;
    194 
    195 	for (i = 0; i < __arraycount(tests); ++i) {
    196 		actual[0] = '\0';
    197 		lc->n_sep_by_space = i;
    198 
    199 		for (j = 0; j < 5; ++j) {
    200 			lc->n_sign_posn = j;
    201 
    202 			strfmon(buf, sizeof(buf) - 1, "[%n] ", -123.0);
    203 			strlcat(actual, buf, sizeof(actual));
    204 		}
    205 
    206 		actual[strlen(actual) - 1] = '\0';
    207 		ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
    208 		    "sep_by_space = %zu", i);
    209 	}
    210 }
    211 
    212 ATF_TC(strfmon_cs_precedes_1);
    213 
    214 ATF_TC_HEAD(strfmon_cs_precedes_1, tc)
    215 {
    216 	atf_tc_set_md_var(tc, "descr",
    217 	    "sep_by_space x sign_posn when cs_precedes = 1");
    218 }
    219 
    220 ATF_TC_BODY(strfmon_cs_precedes_1, tc)
    221 {
    222 	const struct {
    223 		const char *expected;
    224 	} tests[] = {
    225 	    /* sep_by_space x sign_posn */
    226 	    { "[($123.00)] [-$123.00] [$123.00-] [-$123.00] [$-123.00]" },
    227 	    { "[($ 123.00)] [-$ 123.00] [$ 123.00-] [-$ 123.00] [$- 123.00]" },
    228 	    { "[($123.00)] [- $123.00] [$123.00 -] [- $123.00] [$ -123.00]" },
    229 	};
    230 	size_t i, j;
    231 	struct lconv *lc;
    232 	char actual[100], buf[100];
    233 
    234 	if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
    235 		atf_tc_skip("unable to setlocale()");
    236 
    237 	lc = localeconv();
    238 	lc->n_cs_precedes = 1;
    239 
    240 	for (i = 0; i < __arraycount(tests); ++i) {
    241 		actual[0] = '\0';
    242 		lc->n_sep_by_space = i;
    243 
    244 		for (j = 0; j < 5; ++j) {
    245 			lc->n_sign_posn = j;
    246 
    247 			strfmon(buf, sizeof(buf) - 1, "[%n] ", -123.0);
    248 			strlcat(actual, buf, sizeof(actual));
    249 		}
    250 
    251 		actual[strlen(actual) - 1] = '\0';
    252 		ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
    253 		    "sep_by_space = %zu", i);
    254 	}
    255 }
    256 
    257 ATF_TC(strfmon_international_currency_code);
    258 ATF_TC_HEAD(strfmon_international_currency_code, tc)
    259 {
    260 	atf_tc_set_md_var(tc, "descr",
    261 	    "checks strfmon international currency code");
    262 }
    263 
    264 ATF_TC_BODY(strfmon_international_currency_code, tc)
    265 {
    266 	const struct {
    267 		const char *locale;
    268 		const char *expected;
    269 	} tests[] = {
    270 	    { "en_US.UTF-8", "[USD123.45]" },
    271 	    { "de_DE.UTF-8", "[123,45 EUR]" },
    272 	    { "C", "[123.45]" },
    273 	};
    274 	size_t i;
    275 	char actual[100];
    276 
    277 	for (i = 0; i < __arraycount(tests); ++i) {
    278 		if (setlocale(LC_MONETARY, tests[i].locale) == NULL)
    279 			atf_tc_skip("unable to setlocale()");
    280 
    281 		strfmon(actual, sizeof(actual) - 1, "[%i]", 123.45);
    282 		ATF_CHECK_STREQ(tests[i].expected, actual);
    283 	}
    284 }
    285 
    286 ATF_TP_ADD_TCS(tp)
    287 {
    288 
    289 	ATF_TP_ADD_TC(tp, strfmon_locale);
    290 	ATF_TP_ADD_TC(tp, strfmon_pad);
    291 	ATF_TP_ADD_TC(tp, strfmon_locale_thousands);
    292 	ATF_TP_ADD_TC(tp, strfmon_examples);
    293 	ATF_TP_ADD_TC(tp, strfmon_cs_precedes_0);
    294 	ATF_TP_ADD_TC(tp, strfmon_cs_precedes_1);
    295 	ATF_TP_ADD_TC(tp, strfmon_international_currency_code);
    296 
    297 	return atf_no_error();
    298 }
    299