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