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@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 41ATF_TC(strfmon_locale); 42 43ATF_TC_HEAD(strfmon_locale, tc) 44{ 45 atf_tc_set_md_var(tc, "descr", 46 "Checks strfmon_l under different locales"); 47} 48 49ATF_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 72ATF_TC(strfmon_pad); 73 74ATF_TC_HEAD(strfmon_pad, tc) 75{ 76 atf_tc_set_md_var(tc, "descr", "Checks strfmon padding"); 77} 78 79ATF_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 88ATF_TC(strfmon_locale_thousands); 89 90ATF_TC_HEAD(strfmon_locale_thousands, tc) 91{ 92 atf_tc_set_md_var(tc, "descr", 93 "Checks strfmon locale thousands separator"); 94} 95 96ATF_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 126ATF_TC(strfmon_examples); 127ATF_TC_HEAD(strfmon_examples, tc) { 128 atf_tc_set_md_var(tc, "descr", 129 "Checks strfmon field formats"); 130} 131 132ATF_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 167ATF_TC(strfmon_cs_precedes_0); 168 169ATF_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 175ATF_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 212ATF_TC(strfmon_cs_precedes_1); 213 214ATF_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 220ATF_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 257ATF_TC(strfmon_international_currency_code); 258ATF_TC_HEAD(strfmon_international_currency_code, tc) 259{ 260 atf_tc_set_md_var(tc, "descr", 261 "checks strfmon international currency code"); 262} 263 264ATF_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 286ATF_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