18c1c50ffSConrad Meyer /*- 28c1c50ffSConrad Meyer * Copyright (C) 2018 Conrad Meyer <cem@FreeBSD.org> 38c1c50ffSConrad Meyer * All rights reserved. 48c1c50ffSConrad Meyer * 58c1c50ffSConrad Meyer * Redistribution and use in source and binary forms, with or without 68c1c50ffSConrad Meyer * modification, are permitted provided that the following conditions 78c1c50ffSConrad Meyer * are met: 88c1c50ffSConrad Meyer * 1. Redistributions of source code must retain the above copyright 98c1c50ffSConrad Meyer * notice, this list of conditions and the following disclaimer. 108c1c50ffSConrad Meyer * 2. Redistributions in binary form must reproduce the above copyright 118c1c50ffSConrad Meyer * notice, this list of conditions and the following disclaimer in the 128c1c50ffSConrad Meyer * documentation and/or other materials provided with the distribution. 138c1c50ffSConrad Meyer * 148c1c50ffSConrad Meyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 158c1c50ffSConrad Meyer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 168c1c50ffSConrad Meyer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 178c1c50ffSConrad Meyer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 188c1c50ffSConrad Meyer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 198c1c50ffSConrad Meyer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 208c1c50ffSConrad Meyer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 218c1c50ffSConrad Meyer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 228c1c50ffSConrad Meyer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 238c1c50ffSConrad Meyer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 248c1c50ffSConrad Meyer * SUCH DAMAGE. 258c1c50ffSConrad Meyer */ 268c1c50ffSConrad Meyer 278c1c50ffSConrad Meyer #include <sys/cdefs.h> 288c1c50ffSConrad Meyer __FBSDID("$FreeBSD$"); 298c1c50ffSConrad Meyer 303f97d37aSJose Luis Duran #include <sys/param.h> 313f97d37aSJose Luis Duran 328c1c50ffSConrad Meyer #include <locale.h> 338c1c50ffSConrad Meyer #include <monetary.h> 348c1c50ffSConrad Meyer #include <stdio.h> 358c1c50ffSConrad Meyer 368c1c50ffSConrad Meyer #include <atf-c.h> 378c1c50ffSConrad Meyer 388c1c50ffSConrad Meyer ATF_TC_WITHOUT_HEAD(strfmon_locale_thousands); 398c1c50ffSConrad Meyer ATF_TC_BODY(strfmon_locale_thousands, tc) 408c1c50ffSConrad Meyer { 418c1c50ffSConrad Meyer char actual[40], expected[40]; 428c1c50ffSConrad Meyer struct lconv *lc; 438c1c50ffSConrad Meyer const char *ts; 448c1c50ffSConrad Meyer double n; 458c1c50ffSConrad Meyer 468c1c50ffSConrad Meyer setlocale(LC_MONETARY, "sv_SE.UTF-8"); 478c1c50ffSConrad Meyer 488c1c50ffSConrad Meyer lc = localeconv(); 498c1c50ffSConrad Meyer 508c1c50ffSConrad Meyer ts = lc->mon_thousands_sep; 518c1c50ffSConrad Meyer if (strlen(ts) == 0) 528c1c50ffSConrad Meyer ts = lc->thousands_sep; 538c1c50ffSConrad Meyer 548c1c50ffSConrad Meyer if (strlen(ts) < 2) 558c1c50ffSConrad Meyer atf_tc_skip("multi-byte thousands-separator not found"); 568c1c50ffSConrad Meyer 578c1c50ffSConrad Meyer n = 1234.56; 58edcee003SJose Luis Duran strfmon(actual, sizeof(actual) - 1, "%i", n); 598c1c50ffSConrad Meyer 608c1c50ffSConrad Meyer strcpy(expected, "1"); 618c1c50ffSConrad Meyer strlcat(expected, ts, sizeof(expected)); 628c1c50ffSConrad Meyer strlcat(expected, "234", sizeof(expected)); 638c1c50ffSConrad Meyer 64f91301ccSJose Luis Duran /* We're just testing the thousands separator, not all of strfmon. */ 658c1c50ffSConrad Meyer actual[strlen(expected)] = '\0'; 668c1c50ffSConrad Meyer ATF_CHECK_STREQ(expected, actual); 678c1c50ffSConrad Meyer } 688c1c50ffSConrad Meyer 693f97d37aSJose Luis Duran ATF_TC_WITHOUT_HEAD(strfmon_examples); 703f97d37aSJose Luis Duran ATF_TC_BODY(strfmon_examples, tc) 713f97d37aSJose Luis Duran { 723f97d37aSJose Luis Duran const struct { 733f97d37aSJose Luis Duran const char *format; 743f97d37aSJose Luis Duran const char *expected; 753f97d37aSJose Luis Duran } tests[] = { 763f97d37aSJose Luis Duran { "%n", "[$123.45] [-$123.45] [$3,456.78]" }, 7734f88528SJose Luis Duran { "%11n", "[ $123.45] [ -$123.45] [ $3,456.78]" }, 783f97d37aSJose Luis Duran { "%#5n", "[ $ 123.45] [-$ 123.45] [ $ 3,456.78]" }, 793f97d37aSJose Luis Duran { "%=*#5n", "[ $***123.45] [-$***123.45] [ $*3,456.78]" }, 803f97d37aSJose Luis Duran { "%=0#5n", "[ $000123.45] [-$000123.45] [ $03,456.78]" }, 813f97d37aSJose Luis Duran { "%^#5n", "[ $ 123.45] [-$ 123.45] [ $ 3456.78]" }, 823f97d37aSJose Luis Duran { "%^#5.0n", "[ $ 123] [-$ 123] [ $ 3457]" }, 833f97d37aSJose Luis Duran { "%^#5.4n", "[ $ 123.4500] [-$ 123.4500] [ $ 3456.7810]" }, 84947efadcSJose Luis Duran { "%(#5n", "[ $ 123.45 ] [($ 123.45)] [ $ 3,456.78 ]" }, 85947efadcSJose Luis Duran { "%!(#5n", "[ 123.45 ] [( 123.45)] [ 3,456.78 ]" }, 863f97d37aSJose Luis Duran { "%-14#5.4n", "[ $ 123.4500 ] [-$ 123.4500 ] [ $ 3,456.7810 ]" }, 873f97d37aSJose Luis Duran { "%14#5.4n", "[ $ 123.4500] [ -$ 123.4500] [ $ 3,456.7810]" }, 883f97d37aSJose Luis Duran }; 893f97d37aSJose Luis Duran size_t i; 903f97d37aSJose Luis Duran char actual[100], format[50]; 913f97d37aSJose Luis Duran 923f97d37aSJose Luis Duran if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL) 933f97d37aSJose Luis Duran atf_tc_skip("unable to setlocale()"); 943f97d37aSJose Luis Duran 953f97d37aSJose Luis Duran for (i = 0; i < nitems(tests); ++i) { 963f97d37aSJose Luis Duran snprintf(format, sizeof(format), "[%s] [%s] [%s]", 973f97d37aSJose Luis Duran tests[i].format, tests[i].format, tests[i].format); 98edcee003SJose Luis Duran strfmon(actual, sizeof(actual) - 1, format, 993f97d37aSJose Luis Duran 123.45, -123.45, 3456.781); 1003f97d37aSJose Luis Duran ATF_CHECK_STREQ_MSG(tests[i].expected, actual, 1013f97d37aSJose Luis Duran "[%s]", tests[i].format); 1023f97d37aSJose Luis Duran } 1033f97d37aSJose Luis Duran } 1043f97d37aSJose Luis Duran 1053f97d37aSJose Luis Duran ATF_TC(strfmon_cs_precedes_0); 1063f97d37aSJose Luis Duran ATF_TC_HEAD(strfmon_cs_precedes_0, tc) 1073f97d37aSJose Luis Duran { 1083f97d37aSJose Luis Duran atf_tc_set_md_var(tc, "descr", 1093f97d37aSJose Luis Duran "sep_by_space x sign_posn when cs_precedes = 0"); 1103f97d37aSJose Luis Duran } 1113f97d37aSJose Luis Duran ATF_TC_BODY(strfmon_cs_precedes_0, tc) 1123f97d37aSJose Luis Duran { 1133f97d37aSJose Luis Duran const struct { 1143f97d37aSJose Luis Duran const char *expected; 1153f97d37aSJose Luis Duran } tests[] = { 1163f97d37aSJose Luis Duran /* sep_by_space x sign_posn */ 1173f97d37aSJose Luis Duran { "[(123.00$)] [-123.00$] [123.00$-] [123.00-$] [123.00$-]" }, 1183f97d37aSJose Luis Duran { "[(123.00 $)] [-123.00 $] [123.00 $-] [123.00 -$] [123.00 $-]" }, 119750fe3e6SJose Luis Duran { "[(123.00$)] [- 123.00$] [123.00$ -] [123.00- $] [123.00$ -]" }, 1203f97d37aSJose Luis Duran }; 1213f97d37aSJose Luis Duran size_t i, j; 1223f97d37aSJose Luis Duran struct lconv *lc; 1233f97d37aSJose Luis Duran char actual[100], buf[100]; 1243f97d37aSJose Luis Duran 1253f97d37aSJose Luis Duran if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL) 1263f97d37aSJose Luis Duran atf_tc_skip("unable to setlocale()"); 1273f97d37aSJose Luis Duran 1283f97d37aSJose Luis Duran lc = localeconv(); 1293f97d37aSJose Luis Duran lc->n_cs_precedes = 0; 1303f97d37aSJose Luis Duran 1313f97d37aSJose Luis Duran for (i = 0; i < nitems(tests); ++i) { 1323f97d37aSJose Luis Duran actual[0] = '\0'; 1333f97d37aSJose Luis Duran lc->n_sep_by_space = i; 1343f97d37aSJose Luis Duran 1353f97d37aSJose Luis Duran for (j = 0; j < 5; ++j) { 1363f97d37aSJose Luis Duran lc->n_sign_posn = j; 1373f97d37aSJose Luis Duran 138edcee003SJose Luis Duran strfmon(buf, sizeof(buf) - 1, "[%n] ", -123.0); 1393f97d37aSJose Luis Duran strlcat(actual, buf, sizeof(actual)); 1403f97d37aSJose Luis Duran } 1413f97d37aSJose Luis Duran 1423f97d37aSJose Luis Duran actual[strlen(actual) - 1] = '\0'; 1433f97d37aSJose Luis Duran ATF_CHECK_STREQ_MSG(tests[i].expected, actual, 1443f97d37aSJose Luis Duran "sep_by_space = %zu", i); 1453f97d37aSJose Luis Duran } 1463f97d37aSJose Luis Duran } 1473f97d37aSJose Luis Duran 1483f97d37aSJose Luis Duran ATF_TC(strfmon_cs_precedes_1); 1493f97d37aSJose Luis Duran ATF_TC_HEAD(strfmon_cs_precedes_1, tc) 1503f97d37aSJose Luis Duran { 1513f97d37aSJose Luis Duran atf_tc_set_md_var(tc, "descr", 1523f97d37aSJose Luis Duran "sep_by_space x sign_posn when cs_precedes = 1"); 1533f97d37aSJose Luis Duran } 1543f97d37aSJose Luis Duran ATF_TC_BODY(strfmon_cs_precedes_1, tc) 1553f97d37aSJose Luis Duran { 1563f97d37aSJose Luis Duran const struct { 1573f97d37aSJose Luis Duran const char *expected; 1583f97d37aSJose Luis Duran } tests[] = { 1593f97d37aSJose Luis Duran /* sep_by_space x sign_posn */ 1603f97d37aSJose Luis Duran { "[($123.00)] [-$123.00] [$123.00-] [-$123.00] [$-123.00]" }, 1613f97d37aSJose Luis Duran { "[($ 123.00)] [-$ 123.00] [$ 123.00-] [-$ 123.00] [$- 123.00]" }, 1623f97d37aSJose Luis Duran { "[($123.00)] [- $123.00] [$123.00 -] [- $123.00] [$ -123.00]" }, 1633f97d37aSJose Luis Duran }; 1643f97d37aSJose Luis Duran size_t i, j; 1653f97d37aSJose Luis Duran struct lconv *lc; 1663f97d37aSJose Luis Duran char actual[100], buf[100]; 1673f97d37aSJose Luis Duran 1683f97d37aSJose Luis Duran if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL) 1693f97d37aSJose Luis Duran atf_tc_skip("unable to setlocale()"); 1703f97d37aSJose Luis Duran 1713f97d37aSJose Luis Duran lc = localeconv(); 1723f97d37aSJose Luis Duran lc->n_cs_precedes = 1; 1733f97d37aSJose Luis Duran 1743f97d37aSJose Luis Duran for (i = 0; i < nitems(tests); ++i) { 1753f97d37aSJose Luis Duran actual[0] = '\0'; 1763f97d37aSJose Luis Duran lc->n_sep_by_space = i; 1773f97d37aSJose Luis Duran 1783f97d37aSJose Luis Duran for (j = 0; j < 5; ++j) { 1793f97d37aSJose Luis Duran lc->n_sign_posn = j; 1803f97d37aSJose Luis Duran 181edcee003SJose Luis Duran strfmon(buf, sizeof(buf) - 1, "[%n] ", -123.0); 1823f97d37aSJose Luis Duran strlcat(actual, buf, sizeof(actual)); 1833f97d37aSJose Luis Duran } 1843f97d37aSJose Luis Duran 1853f97d37aSJose Luis Duran actual[strlen(actual) - 1] = '\0'; 1863f97d37aSJose Luis Duran ATF_CHECK_STREQ_MSG(tests[i].expected, actual, 1873f97d37aSJose Luis Duran "sep_by_space = %zu", i); 1883f97d37aSJose Luis Duran } 1893f97d37aSJose Luis Duran } 1903f97d37aSJose Luis Duran 1913f97d37aSJose Luis Duran ATF_TC_WITHOUT_HEAD(strfmon_international_currency_code); 1923f97d37aSJose Luis Duran ATF_TC_BODY(strfmon_international_currency_code, tc) 1933f97d37aSJose Luis Duran { 1943f97d37aSJose Luis Duran const struct { 1953f97d37aSJose Luis Duran const char *locale; 1963f97d37aSJose Luis Duran const char *expected; 1973f97d37aSJose Luis Duran } tests[] = { 1986da51e19SJose Luis Duran { "en_US.UTF-8", "[USD123.45]" }, 1996da51e19SJose Luis Duran { "de_DE.UTF-8", "[123,45 EUR]" }, 2009e03b903SJose Luis Duran { "C", "[123.45]" }, 2013f97d37aSJose Luis Duran }; 2023f97d37aSJose Luis Duran size_t i; 2033f97d37aSJose Luis Duran char actual[100]; 2043f97d37aSJose Luis Duran 2053f97d37aSJose Luis Duran for (i = 0; i < nitems(tests); ++i) { 2063f97d37aSJose Luis Duran if (setlocale(LC_MONETARY, tests[i].locale) == NULL) 2073f97d37aSJose Luis Duran atf_tc_skip("unable to setlocale()"); 2083f97d37aSJose Luis Duran 209edcee003SJose Luis Duran strfmon(actual, sizeof(actual) - 1, "[%i]", 123.45); 2103f97d37aSJose Luis Duran ATF_CHECK_STREQ(tests[i].expected, actual); 2113f97d37aSJose Luis Duran } 2123f97d37aSJose Luis Duran } 2133f97d37aSJose Luis Duran 21429972f06SJose Luis Duran ATF_TC(strfmon_l); 21529972f06SJose Luis Duran ATF_TC_HEAD(strfmon_l, tc) 21629972f06SJose Luis Duran { 21729972f06SJose Luis Duran atf_tc_set_md_var(tc, "descr", 21829972f06SJose Luis Duran "checks strfmon_l under different locales"); 21929972f06SJose Luis Duran } 22029972f06SJose Luis Duran ATF_TC_BODY(strfmon_l, tc) 22129972f06SJose Luis Duran { 22229972f06SJose Luis Duran const struct { 22329972f06SJose Luis Duran const char *locale; 22429972f06SJose Luis Duran const char *expected; 22529972f06SJose Luis Duran } tests[] = { 22629972f06SJose Luis Duran { "C", "[ **1234.57 ] [ **1234.57 ]" }, 227*621bf918SJose Luis Duran { "de_DE.UTF-8", "[ **1234,57 €] [ **1.234,57 EUR]" }, 228*621bf918SJose Luis Duran { "en_GB.UTF-8", "[ £**1234.57] [ GBP**1,234.57]" }, 22929972f06SJose Luis Duran }; 23029972f06SJose Luis Duran locale_t loc; 23129972f06SJose Luis Duran size_t i; 23229972f06SJose Luis Duran char buf[100]; 23329972f06SJose Luis Duran 23429972f06SJose Luis Duran for (i = 0; i < nitems(tests); ++i) { 23529972f06SJose Luis Duran loc = newlocale(LC_MONETARY_MASK, tests[i].locale, NULL); 23629972f06SJose Luis Duran ATF_REQUIRE(loc != NULL); 23729972f06SJose Luis Duran 23829972f06SJose Luis Duran strfmon_l(buf, sizeof(buf) - 1, loc, "[%^=*#6n] [%=*#6i]", 23929972f06SJose Luis Duran 1234.567, 1234.567); 24029972f06SJose Luis Duran ATF_REQUIRE_STREQ(tests[i].expected, buf); 24129972f06SJose Luis Duran 24229972f06SJose Luis Duran freelocale(loc); 24329972f06SJose Luis Duran } 24429972f06SJose Luis Duran } 24529972f06SJose Luis Duran 2468c1c50ffSConrad Meyer ATF_TP_ADD_TCS(tp) 2478c1c50ffSConrad Meyer { 2488c1c50ffSConrad Meyer ATF_TP_ADD_TC(tp, strfmon_locale_thousands); 2493f97d37aSJose Luis Duran ATF_TP_ADD_TC(tp, strfmon_examples); 2503f97d37aSJose Luis Duran ATF_TP_ADD_TC(tp, strfmon_cs_precedes_0); 2513f97d37aSJose Luis Duran ATF_TP_ADD_TC(tp, strfmon_cs_precedes_1); 2523f97d37aSJose Luis Duran ATF_TP_ADD_TC(tp, strfmon_international_currency_code); 25329972f06SJose Luis Duran ATF_TP_ADD_TC(tp, strfmon_l); 2548c1c50ffSConrad Meyer return (atf_no_error()); 2558c1c50ffSConrad Meyer } 256