[EMAIL PROTECTED] wrote:
Author: elemings
Date: Tue May  6 16:08:12 2008
New Revision: 653949

URL: http://svn.apache.org/viewvc?rev=653949&view=rev
Log:
2008-05-06  Eric Lemings <[EMAIL PROTECTED]>

        STDCXX-881
        * branches/4.2.x/tests/localization/22.locale.collate.cpp:
          ^^^^^^^^^^^^^^

I don't think we want this part in the ChangeLog entries.
Only the subdirectory of the project's root directory, or
TOPDIR in the GNUMakefile terminology.

Martin

        Migrated older test from Perforce repository to new test driver
        in Subversion repository.


Added:
    stdcxx/branches/4.2.x/tests/localization/22.locale.collate.cpp

Added: stdcxx/branches/4.2.x/tests/localization/22.locale.collate.cpp
URL: 
http://svn.apache.org/viewvc/stdcxx/branches/4.2.x/tests/localization/22.locale.collate.cpp?rev=653949&view=auto
==============================================================================
--- stdcxx/branches/4.2.x/tests/localization/22.locale.collate.cpp (added)
+++ stdcxx/branches/4.2.x/tests/localization/22.locale.collate.cpp Tue May  6 
16:08:12 2008
@@ -0,0 +1,1118 @@
+/***************************************************************************
+ *
+ * 22.locale.collate.cpp -- tests for collate-facet member functions
+ *
+ * $Id$
+ *
+ ***************************************************************************
+ *
+ * Licensed to the Apache Software  Foundation (ASF) under one or more
+ * contributor  license agreements.  See  the NOTICE  file distributed
+ * with  this  work  for  additional information  regarding  copyright
+ * ownership.   The ASF  licenses this  file to  you under  the Apache
+ * License, Version  2.0 (the  "License"); you may  not use  this file
+ * except in  compliance with the License.   You may obtain  a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the  License is distributed on an  "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
+ * implied.   See  the License  for  the  specific language  governing
+ * permissions and limitations under the License.
+ *
+ * Copyright 1994-2008 Rogue Wave Software.
+ *
+ **************************************************************************/
+
+#include <locale>     // for collate, locale
+#include <string>     // for string
+
+#include <algorithm>  // for sort and unique
+#include <climits>    // for UCHAR_MAX
+#include <clocale>    // for LC_COLLATE, setlocale
+#include <cstdlib>    // for exit()
+#include <cstdio>     // for fprintf()
+#include <cstring>    // for strcmp(), strcoll(), ...
+#include <cwchar>     // for wcscoll()
+
+#include <driver.h>
+#include <environ.h>
+#include <file.h>
+#include <rw_locale.h>
+#include <rw_process.h>
+
+
+#if _RWSTD_PATH_SEP == '/'
+#  define SLASH                 "/"
+#else
+#  define SLASH                 "\\"
+#endif
+
+// strings declared extern to work around a SunPro bug (PR #28124)
+// get the source root
+#define RELPATH                 "etc" SLASH "nls"
+#define TESTS_ETC_PATH          "tests" SLASH "etc"
+
+// the root of the locale directory (RWSTD_LOCALE_ROOT)
+#define LOCALE_ROOT             "RWSTD_LOCALE_ROOT"
+const char* locale_root;
+
+#define LC_COLLATE_SRC          "LC_COLLATE.src"
+#define LC_COLLATE_CM           "LC_COLLATE.cm"
+#define TEST_LOCALE_NAME        "test.locale"
+
+/**************************************************************************/
+
+// These overloads are necessary in our template
+// functions so that we can make a single function call reguardless
+// of the charT we are using
+
+int c_strcoll (const char* s1, const char* s2)
+{
+    const int ret = std::strcoll(s1, s2);
+    return ret ? ret > 0 ? 1 : -1 : 0;
+}
+
+std::size_t c_xfrm (char* to, const char* from, std::size_t size)
+{
+    char safety_buf [8];
+    if (0 == to && 0 == size) {
+        // prevent buggy implementations (such as MSVC 8) from trying
+        // to write to the destination buffer even though it's 0 and
+        // its size is zero (see stdcxx-69)
+        to = safety_buf;
+        *to = '\0';
+    }
+
+    std::size_t n = std::strxfrm (to, from, size);
+
+    if (to)
+        n = std::strlen (to);
+
+    return n;
+}
+
+std::size_t c_strlen (const char* s1)
+{
+    return std::strlen (s1);
+}
+
+const char* narrow (char* dst, const char* src)
+{
+    if (src == dst || !src || !dst)
+        return src;
+
+    std::memcpy (dst, src, std::strlen (src) + 1);
+    return dst;
+}
+
+const char* widen (char* dst, const char* src)
+{
+    if (src == dst || !src || !dst)
+        return src;
+
+    std::memcpy (dst, src, std::strlen (src) + 1);
+    return dst;
+}
+
+#ifndef _RWSTD_NO_WCHAR_T
+
+int c_strcoll (const wchar_t* s1, const wchar_t* s2)
+{
+    const int ret = std::wcscoll(s1, s2);
+    return ret ? ret > 0 ? 1 : -1 : 0;
+}
+
+std::size_t c_xfrm (wchar_t* to, const wchar_t* from, std::size_t size)
+{
+#if !defined (_MSC_VER) || _MSC_VER > 1200
+
+    wchar_t safety_buf [8];
+    if (0 == to && 0 == size) {
+        // prevent buggy implementations (such as MSVC 8) from trying
+        // to write to the destination buffer even though it's 0 and
+        // its size is zero (see stdcxx-69)
+        to = safety_buf;
+        *to = L'\0';
+    }
+
+    std::size_t n = std::wcsxfrm (to, from, size);
+
+    if (to)
+        n = std::wcslen (to);
+
+#else   // MSVC 6 and prior
+
+    // working around an MSVC 6.0 libc bug (PR #26437)
+
+    if (to) {
+        std::size_t n = std::wcsxfrm (to, from, size);
+
+        n = std::wcslen (to);
+
+        return n;
+    }
+
+    wchar_t tmp [1024];
+
+    std::size_t n = std::wcslen (from);
+
+    _RWSTD_ASSERT (n < sizeof tmp / sizeof *tmp);
+
+    std::wcscpy (tmp, from);
+
+    std::wcsxfrm (tmp, from, sizeof tmp / sizeof *tmp);
+
+    n = std::wcslen (tmp);
+
+#endif   // MSVC 6
+
+    return n;
+}
+
+std::size_t c_strlen (const wchar_t* s1)
+{
+    return std::wcslen (s1);
+}
+
+const wchar_t* widen (wchar_t* dst, const char* src)
+{
+    static wchar_t buf [4096];
+
+    if (!src)
+        return 0;
+
+    if (!dst)
+        dst = buf;
+
+    std::size_t len = std::strlen (src);
+
+    _RWSTD_ASSERT (len < sizeof buf /sizeof *buf);
+
+    len = std::mbstowcs (dst, src, sizeof buf / sizeof *buf);
+
+    if (std::size_t (-1) == len)
+        *dst = 0;
+
+    return dst;
+}
+
+const char* narrow (char* dst, const wchar_t* src)
+{
+    static char buf [4096];
+
+    if (!src)
+        return 0;
+
+    if (!dst)
+        dst = buf;
+
+    std::size_t len = std::wcslen (src);
+
+    _RWSTD_ASSERT (len < sizeof buf);
+
+    len = std::wcstombs (dst, src, sizeof buf / sizeof *buf);
+
+    if (std::size_t (-1) == len)
+        *dst = 0;
+
+    return dst;
+}
+
+#endif   //_RWSTD_NO_WCHAR_T
+
+/**************************************************************************/
+
+template <class charT>
+/*static*/ void
+gen_str (charT* str, std::size_t size)
+{
+    // generate a random string with the given size
+    if (!size)
+        return;
+
+    // use ASCII characters in the printable range
+    for (std::size_t i = 0; i != size - 1; ++i)
+        str [i] = ' ' + std::rand () % ('~' - ' ');
+
+    str [size - 1] = charT ();
+}
+
+/**************************************************************************/
+
+template <class charT>
+/*static*/ void
+check_libc (const char* charTname)
+{
+    // the libc implementation of the library should act the same as
+    // the c-library.  Go through all the locales, generate some random
+    // strings and make sure that the following holds true:
+    // transform acts like strxfrm and wcsxfrm,
+    // compare acts like strcoll and wcscoll
+
+    int nfail [3] = { 0 };
+
+    rw_info (0, __FILE__, __LINE__,
+             "libc std::collate<%s>::transform ()", charTname);
+
+    rw_info (0, __FILE__, __LINE__,
+             "libc std::collate<%s>::compare ()", charTname);
+
+    rw_info (0, __FILE__, __LINE__,
+             "std::collate<%s>::hash ()", charTname);
+
+    for (const char* locname = rw_locales (LC_COLLATE);
+         *locname; locname += std::strlen (locname) + 1) {
+
+        _TRY {
+            std::setlocale (LC_COLLATE, locname);
+            int max = MB_CUR_MAX;
+            if (max > 1)
+                continue;
+
+            std::locale loc;
+
+            _TRY {
+                loc = std::locale (locname);
+            }
+            _CATCH (...) {
+                rw_assert (false, __FILE__, __LINE__,
+                           "std::locale(\"%s\") unexpectedly threw "
+                           "an exception", locname);
+                continue;
+            }
+
+            const std::collate<charT> &co =
+                _STD_USE_FACET (std::collate<charT>, loc);
+            co._C_opts |= co._C_use_libc;
+            co._C_opts &= ~co._C_use_libstd;
+
+            // now the locale is set up so lets test the transform and
+            // compare functions
+
+            for (int loop_cntrl = 0; loop_cntrl < 10; loop_cntrl++) {
+
+#define STR_SIZE 16
+
+                charT str1 [STR_SIZE] = { 0 };
+                charT str2 [STR_SIZE] = { 0 };
+
+                // generate two random NUL-terminated strings
+                gen_str (str1, sizeof str1 / sizeof *str1);
+                gen_str (str2, sizeof str2 / sizeof *str2);
+
+                // call transform on the generated string
+                // not including the terminating NUL
+                const std::basic_string <charT, std::char_traits<charT>,
+                    std::allocator<charT> > out =
+                    co.transform (str1, str1 + sizeof str1 / sizeof *str1 - 1);
+
+                // get the size of the buffer needed to hold the
+                // transformed string (with the terminating NUL)
+                std::size_t size = 1U + c_xfrm (0, str1, 0);
+
+                // prevent errors caused by huge return values (e.g., MSVC)
+                if (size > STR_SIZE * 64)
+                    size = 0;
+
+                std::basic_string <charT, std::char_traits<charT>,
+                    std::allocator<charT> > c_out;
+
+                if (size) {
+                    c_out.resize (size);
+
+                    // call the C-library transform function
+                    size = c_xfrm (&c_out [0], str1, size);
+
+                    if (size > STR_SIZE * 64)
+                        size = 0;
+
+                    // shrink to fit (chop off the terminating NUL)
+                    c_out.resize (size);
+                }
+
+                // make sure the output is the same
+                if (out != c_out) {
+                    nfail[0]++;
+                    rw_assert (false, __FILE__, __LINE__,
+                               "%d. collate<%s>::transform(%s, ...) "
+                               "== %{S}, got %{S} in locale(\"%s\")",
+                               loop_cntrl, charTname, str1,
+                               &c_out, &out, locname);
+                }
+
+                // now call compare on the two generated strings
+                int ret1 = co.compare (str1, str1 + sizeof str1 / sizeof *str1,
+                                       str2, str2 + sizeof str2 / sizeof 
*str2);
+
+                // call the C-library comparison function
+                int ret2 = c_strcoll (str1, str2);
+
+                // make sure the results are the same
+                if (ret1 != ret2) {
+                    nfail [1]++;
+                    rw_assert (false, __FILE__, __LINE__,
+                               "%d. collate<%s>::compare(%s, ..., %s, ...) "
+                               "== %d, got %d in locale(\"%s\")",
+                               loop_cntrl, charTname, str1,
+                               str2, ret2, ret1, locname);
+                }
+
+                // two strings that compare identically must hash
+                // identically as well.  Calling hash on the same string is
+                // not very conclusive but generating strings that have exactly
+                // the same weights is not possible without knowing all the
+                // weight orderings
+                const long hashNum1 =
+                    co.hash (str1, str1 + sizeof str1 / sizeof *str1);
+
+                const long hashNum2 =
+                    co.hash (str1, str1 + sizeof str1 / sizeof *str1);
+
+                if (hashNum1 != hashNum2) {
+                    nfail[2]++;
+                    rw_assert (false, __FILE__, __LINE__,
+                               "%d. collate<%s>::hash(%s, ...) == %d, "
+                               "got %d in locale(\"%s\")",
+                               loop_cntrl, charTname, str1,
+                               hashNum1, hashNum2, locname);
+                }
+
+
+            }
+        }
+        _CATCH (...) {
+            rw_assert (false, __FILE__, __LINE__,
+                       "locale(\"%s\") threw an exception", locname);
+        }
+    }
+
+    rw_assert (0 == nfail [0], __FILE__, __LINE__,
+               "collate<%s>::transform () failed %d times",
+               charTname, nfail [0]);
+
+    rw_assert (0 == nfail [1], __FILE__, __LINE__,
+               "collate<%s>::compare () failed %d times",
+               charTname, nfail [1]);
+
+    rw_assert (0 == nfail [2], __FILE__, __LINE__,
+               "collate<%s>::hash () failed %d times",
+               charTname, nfail [2]);
+}
+
+/**************************************************************************/
+
+static const char*
+make_test_locale ()
+{
+    // create a temporary locale definition file that exercises as
+    // many different parts of the collate standard as possible
+
+    char lc_collate_src_path [L_tmpnam + sizeof LC_COLLATE_SRC + 2];
+    std::strcpy (lc_collate_src_path, locale_root);
+    std::strcat (lc_collate_src_path, SLASH);
+    std::strcat (lc_collate_src_path, LC_COLLATE_SRC);
+
+    std::FILE *fout = std::fopen (lc_collate_src_path, "w");
+
+    const char lc_collate_file[] = {
+        "LC_COLLATE\n"
+        "script <ALL_FORWARD>\n"
+        "collating-element <er> from \"<e><r>\"\n"
+        "collating-element <ic> from \"ic\"\n"
+        "collating-symbol <LETTER>\n"
+        "collating-symbol <COLLATING_ELEMENT>\n"
+        "collating-symbol <DIGIT>\n"
+
+        "order_start forward;backward;forward,position\n"
+        "<LETTER>\n"
+        "<COLLATING_ELEMENT>\n"
+        "<DIGIT>\n"
+
+        "<a> <a> <LETTER> IGNORE\n"
+        "<b> <b> <LETTER> IGNORE\n"
+
+        // "<c>" will have a non-ignored position ordering
+        "<c> <c> <LETTER> <c>\n"
+
+        // try giving "<d>" a many-to-one weight
+        "<d> \"<d><a>\" <LETTER> IGNORE\n"
+
+        // try giving "<e>" a decimal value weight
+        "<e> \\d139 <LETTER> IGNORE\n"
+
+        // try giving "<f>" an octal value weight
+        "<f> \\36 <LETTER> IGNORE\n"
+
+        // try giving "<g>" a hex value weight
+        "<g> \\x3A <LETTER> IGNORE\n"
+
+        "<zero> <zero> <DIGIT> IGNORE\n"
+        "<one> <one> <DIGIT> <zero>\n"
+        "<two> <two> <DIGIT> IGNORE\n"
+        "<three> <three> <DIGIT> IGNORE\n"
+        "<er> <a> <COLLATING_ELEMENT> IGNORE\n"
+
+        // the <ic> collating element will be equivalent to the letter <c>
+        "<ic> <c> <LETTER> <c>\n"
+        "UNDEFINED IGNORE IGNORE IGNORE\n"
+
+        "order_end\n"
+
+        // define a section in which all of the orders are forward orders
+        "order_start <ALL_FORWARD>;forward;forward;forward\n"
+        "<h>\n<i>\n<j>\n<k>\n"
+        "order_end\n"
+
+        // reorder the elementes in the <ALL_FORWARD> section to appear
+        // after the letter "<g>"
+        "reorder-after <g>\n"
+        "<h>\n<i>\n<j>\n<k>\n"
+
+        // try to reorder "<a>" after "<b>"
+        "reorder-after <b>\n"
+        "<a> <a> <LETTER> IGNORE\n"
+        "reorder-end\n"
+
+        "\nEND LC_COLLATE\n"
+    };
+
+    std::fputs (lc_collate_file, fout);
+
+    std::fclose (fout);
+
+    // create a temporary character map file
+
+    char lc_collate_cm_path [L_tmpnam + sizeof LC_COLLATE_CM + 2];
+    std::strcpy (lc_collate_cm_path, locale_root);
+    std::strcat (lc_collate_cm_path, SLASH);
+    std::strcat (lc_collate_cm_path, LC_COLLATE_CM);
+
+    fout = std::fopen (lc_collate_cm_path, "w");
+    pcs_write (fout, 0);
+
+    std::fclose (fout);
+
+    return rw_localedef ("-w", lc_collate_src_path,
+                         lc_collate_cm_path,
+                         TEST_LOCALE_NAME);
+}
+
+/**************************************************************************/
+
+
+template <class charT>
+/*static*/ void
+check_libstd_test_locale (const char* charTname)
+{
+    rw_info (0, __FILE__, __LINE__,
+             "libstd std::collate<%s>::transform () "
+             "collate test database", charTname);
+    rw_info (0, __FILE__, __LINE__,
+             "libstd std::collate<%s>::compare () collate test "
+             "database", charTname);
+    rw_info (0, __FILE__, __LINE__,
+             "libstd std::collate<%s>::hash () collate test "
+             "database", charTname);
+
+    const char* const locname = make_test_locale ();
+    if (locname) {
+
+        std::locale loc;
+
+        _TRY {
+            loc = std::locale (locname);
+        }
+        _CATCH (...) {
+            const char* const var = std::getenv (LOCALE_ROOT);
+
+            rw_assert (false, __FILE__, __LINE__,
+                       "std::locale(\"%s\") unexpectedly threw "
+                       "an exception; " LOCALE_ROOT "=%s",
+                       locname, var ? var : "(null)");
+            return;
+        }
+
+        const std::collate<charT> &co =
+            _STD_USE_FACET (std::collate<charT>, loc);
+        co._C_opts |=  co._C_use_libstd;
+        co._C_opts &= ~co._C_use_libc;
+
+#define IGNORE 0
+
+        // first lets make sure that each character was given the
+        // correct weight for each level.
+
+#undef TEST
+#define TEST(ch, w0, w1, w2, w3, w3_is_fp)   \
+  test_weight_val (charTname, co, charT (ch), w0, w1, w2, w3, w3_is_fp)
+
+        TEST ('a',       6, IGNORE,      2, IGNORE, true);
+        TEST ('b',       5, IGNORE,      2, IGNORE, true);
+        TEST ('c',       7, IGNORE,      2,      7, true);
+        TEST ('d',       8,      6,      2, IGNORE, true);
+        TEST ('e',     139, IGNORE,      2, IGNORE, true);
+        TEST ('f',      30, IGNORE,      2, IGNORE, true);
+        TEST ('g',      58, IGNORE,      2, IGNORE, true);
+        TEST ('h',      12, IGNORE,     12,     12, false);
+        TEST ('i',      13, IGNORE,     13,     13, false);
+        TEST ('j',      14, IGNORE,     14,     14, false);
+        TEST ('k',      15, IGNORE,     15,     15, false);
+        TEST ('0',      16, IGNORE,      4, IGNORE, true);
+        TEST ('1',      17, IGNORE,      4,     16, true);
+        TEST ('2',      18, IGNORE,      4, IGNORE, true);
+        TEST ('3',      19, IGNORE,      4, IGNORE, true);
+        TEST ('l',  IGNORE, IGNORE, IGNORE, IGNORE, true);
+
+        // make sure that strings collate the way we expect them to
+
+        // a should collate greater then b
+        test_string (charTname, co, "a", "b", 1) ;
+
+        // the collating element "er" should collate after 'a' and 'b'
+        // but before 'c'
+        test_string (charTname, co, "er", "a", 1);
+        test_string (charTname, co, "er", "b", 1);
+        test_string (charTname, co, "er", "c", -1);
+
+        // the collating element "ic" should be equivalent to the letter 'c'
+        test_string (charTname, co, "ic", "c", 0);
+
+
+        // two strings that compare identically must hash
+        // identically as well.
+        // since ic and c are equivalent elements string they should hash
+        // the same
+        test_hash (charTname, co, "c", "ic");
+    }
+    else
+        rw_assert (false, __FILE__, __LINE__,
+                   "unable to create a locale database");
+}
+
+/**************************************************************************/
+
+enum { bufsiz = 256 };
+
+template <class charT>
+/*static*/ void
+test_hash (const char* charTname, const std::collate<charT>& co,
+           const char* str1, const char* str2)
+{
+    // convert narrow string to a (possibly) wide representation
+    charT wstrbuf [bufsiz];
+    charT wstrbuf2 [bufsiz];
+
+    const charT* const wstr = widen (wstrbuf, str1);
+    const charT* const wstr2 = widen (wstrbuf2, str2);
+
+    long hashNum1 = co.hash (wstr, wstr + c_strlen (wstr));
+    long hashNum2 = co.hash (wstr2, wstr2 + c_strlen (wstr2));
+
+    if (hashNum1 != hashNum2) {
+        rw_assert (false, __FILE__, __LINE__,
+                   "collate<%s>::hash(%s, ...) returned %d and\n "
+                   "collate<%s>::hash(%s, ...) returned %d",
+                   charTname, str1,
+                   hashNum1, charTname, str2, hashNum2);
+    }
+}
+
+/**************************************************************************/
+
+template <class charT>
+/*static*/ void
+test_string (const char* charTname, const std::collate<charT>& co,
+             const char* str1, const char* str2,
+             int expected_val)
+{
+    // convert narrow string to a (possibly) wide representation
+    charT wstrbuf [bufsiz];
+    charT wstrbuf2 [bufsiz];
+
+    const charT* const wstr = widen (wstrbuf, str1);
+    const charT* const wstr2 = widen (wstrbuf2, str2);
+
+    int ret = co.compare (wstr, wstr + c_strlen (wstr),
+                          wstr2, wstr2 +  c_strlen(wstr2));
+    if (ret != expected_val)
+        rw_assert (false, __FILE__, __LINE__,
+                   "libstd std::collate<%s>::compare"
+                   "(%s, ..., %s, ...) == %d, got %d",
+                   charTname, str1, str2, expected_val, ret);
+}
+
+/**************************************************************************/
+
+template <class charT>
+/*static*/ void
+test_weight_val (const char* charTname, const std::collate<charT>& co,
+                 charT ch, int w1a, int w1b, int w2, int w3, bool w3_is_fp)
+{
+    int w [3][2] = { { w1a, w1b }, { w2, IGNORE }, { w3, IGNORE } };
+
+    typedef std::char_traits<charT>                  Traits;
+    typedef std::allocator<charT>                    Alloc;
+    typedef std::basic_string <charT, Traits, Alloc> String;
+
+    // construct an expected transformed string out of the weight arguments
+    String expected;
+
+    if (sizeof (charT) == sizeof (char)) {
+        for (int i = 0; i < 3; ++i) {
+            for (int k = 0; k < 2; ++k) {
+                if (w [i][k] != IGNORE) {
+                    while (w [i][k] > _RWSTD_CHAR_MAX) {
+                        expected += charT (_RWSTD_CHAR_MAX);
+                        w [i][k] -= _RWSTD_CHAR_MAX;
+                    }
+                    expected += charT (w [i][k]);
+                }
+                else if (i == 2 && k == 0 && w3_is_fp)
+                    expected += charT (_RWSTD_CHAR_MAX);
+            }
+
+            // mark the end of the pass
+            expected += charT (1);
+        }
+    }
+    else {
+        for (int i = 0; i < 3; ++i) {
+            for (int k = 0; k < 2; ++k) {
+                if (w [i][k] != IGNORE) {
+                    expected += charT (w [i][k]);
+                }
+                else if (i == 2 && k == 0 && w3_is_fp)
+                    expected += charT (_RWSTD_WCHAR_MAX);
+            }
+
+            expected += charT (1);
+        }
+    }
+
+    // get the transformed string
+    const String actual = co.transform (&ch, &ch + 1);
+
+    // make sure the strings are equal
+    rw_assert (expected != actual, __FILE__, __LINE__,
+               "collate<%s>::transform (\"%c\", ...) == %{S}, "
+               "got %{S}", charTname, ch, &expected, &actual);
+}
+
+/**************************************************************************/
+
+template <class charT>
+/*static*/ void
+check_libstd (const char* charTname)
+{
+    rw_info (0, __FILE__, __LINE__,
+             "libstd std::collate<%s>::transform () sorting "
+             "file test", charTname);
+
+    rw_info (0, __FILE__, __LINE__,
+             "libstd std::collate<%s>::compare () sorting "
+             "file test", charTname);
+
+
+    // This test works by using a series of sorted input files
+    // we randomize the words in the input files and sort them using
+    // the proper locale's collate facet.  This test will automatically
+    // generate the required locales.
+
+    static const char* const locales[][3] = {
+        //
+        // +-- locale name
+        // |        +-- character set
+        // |        |             +-- input file name
+        // |        |             |
+        // V        V             V
+        { "cs_CZ", "ISO-8859-2", "collate.cs_CZ.in" },   // Czech, Czech Rep.
+        { "da_DK", "ISO-8859-1", "collate.da_DK.in" },   // Danish, Denmark
+        { "en_US", "ISO-8859-1", "collate.en_US.in" },   // English, US
+        { "hr_HR", "ISO-8859-2", "collate.hr_HR.in" },   // Hungarian, Hungary
+        { "sv_SE", "ISO-8859-1", "collate.sv_SE.in" },   // Swedish, Sweden
+        { "th_TH", "TIS-620",    "collate.th_TH.in" }    // Thai, Thailand
+    };
+
+    const std::size_t nlocales = sizeof locales / sizeof *locales;
+
+    typedef std::char_traits<charT>                     Traits;
+    typedef std::allocator<charT>                       Allocator;
+    typedef std::basic_string<charT, Traits, Allocator> String;
+
+    for (std::size_t i = 0; i < nlocales; ++i) {
+
+        const char* const locname =
+            rw_localedef ("-w --no_position",
+                          locales [i][0], locales [i][1], 0);
+
+        if (locname) {
+
+            std::locale loc;
+
+            _TRY {
+                loc = std::locale (locname);
+            }
+            _CATCH (...) {
+                const char* const var = std::getenv (LOCALE_ROOT);
+
+                rw_assert (false, __FILE__, __LINE__,
+                           "std::locale(\"%s\") unexpectedly threw "
+                           "an exception; " LOCALE_ROOT "=%s",
+                           locname, var ? var : "(null)");
+                continue;
+            }
+
+            const std::collate<charT> &co =
+                _STD_USE_FACET (std::collate<charT>, loc);
+
+            co._C_opts |= co._C_use_libstd;
+            co._C_opts &= ~co._C_use_libc;
+
+            typedef std::codecvt<charT, char, std::mbstate_t> CodeCvt;
+
+            const CodeCvt &cvt = _STD_USE_FACET (CodeCvt, loc);
+
+            cvt._C_opts |= cvt._C_use_libstd;
+            cvt._C_opts &= ~cvt._C_use_libc;
+
+            // 'in' holds the strings from the input file and is there
+            // sorting will take place.
+            String in [1000];
+
+            // out holds the strings located in the output file
+            String out [1000];
+
+#define TOPDIR   "TOPDIR"   /* the TOPDIR environment variable */
+
+            const char* in_path = std::getenv (TOPDIR);
+            if (!in_path || !*in_path) {
+                std::fprintf (stderr, "TOPDIR not defined or empty");
+
+                std::exit (1);
+            }
+
+            std::string path (in_path);
+            path += SLASH TESTS_ETC_PATH SLASH;
+            path += locales [i][2];
+
+            std::FILE* const f = std::fopen (path.c_str (), "r");
+            if (!f) {
+                rw_assert (false, __FILE__, __LINE__,
+                           "file \"%s\" could not be opened", path.c_str ());
+                break;
+            }
+
+            std::size_t j = 0;
+            while (1) {
+                char next_line [bufsiz];
+
+                if (0 != std::fgets (next_line, bufsiz, f)) {
+
+                    std::size_t line_len = std::strlen (next_line);
+
+                    // get rid of the newline character
+                    next_line [--line_len] = '\0';
+
+                    // convert from external to internal encoding
+                    // (both of which might be the same type)
+                    charT to [bufsiz];
+                    const char* from_next;
+                    charT*      to_next;
+
+                    static std::mbstate_t initial;
+                    std::mbstate_t mbs = initial;
+
+                    const std::codecvt_base::result res =
+                        cvt.in (mbs,
+                                next_line, next_line + line_len + 1,
+                                from_next,
+                                to, to + sizeof to / sizeof *to,
+                                to_next);
+
+                    if (cvt.ok == res) {
+                        in [j]  = to;
+                        out [j] = to;
+                    }
+                    else if (cvt.noconv == res) {
+                        in [j]  = (charT*)next_line;
+                        out [j] = (charT*)next_line;
+                    }
+
+                    j++;
+                }
+                else
+                    break;
+            }
+            // close the file
+            std::fclose (f);
+
+            // now bubble sort the items in the array
+            std::size_t idx;
+            std::size_t idx2;
+            String tmp;
+            String tmp2;
+
+            bool flipped;
+
+            if (j > 1) {
+                idx = 1;
+                do {
+                    flipped = false;
+                    for (idx2 = j - 1; idx2 >= idx; --idx2) {
+
+                        const std::size_t idx1 = idx2 - 1;
+
+                        if (co.compare (in [idx1].c_str (),
+                                        in [idx1].c_str () + in [idx1].size (),
+                                        in [idx2].c_str (),
+                                        in [idx2].c_str () + in [idx2].size ())
+                            > 0) {
+                            in [idx1].swap (in [idx2]);
+                            flipped = true;
+                        }
+                    }
+                } while (++idx < j && flipped);
+            }
+
+            // the items are sorted now lets make sure that they are sorted
+            // the same way they are sorted in the output file.
+            std::size_t nfail = 0;
+
+            for (std::size_t k = 0; k < j; ++k) {
+
+                if (in [k] != out [k]) {
+
+                    nfail++;
+
+                    rw_assert (false, __FILE__, __LINE__,
+                               "%{S} != %{S} at line %u of %s",
+                               &out [k], &in [k],
+                               k + 1, locales [i][2]);
+
+                }
+            }
+
+            rw_assert (!nfail, __FILE__, __LINE__,
+                       "collate<%s>::compare() failed %d times",
+                       charTname, nfail);
+        }
+    }
+}
+
+/**************************************************************************/
+
+
+template <class charT>
+/*static*/ void
+check_hash_eff (const char* charTname)
+{
+    // test effectiveness of hash function
+    rw_info (0, __FILE__, __LINE__,
+             "std::collate<%s>::hash () -- effectiveness", charTname);
+
+    // since the same hash algorithm is used for both byname and non-byname
+    // facets, simply set up a std::locale that uses the "C" locale
+    std::locale loc ("C");
+    const std::collate<charT> &co =
+        _STD_USE_FACET (std::collate<charT>, loc);
+
+
+    int nfail = 0;
+
+    charT s[100];
+    bool next = true;
+
+    // generate `N' unique strings and hash them, storing each value
+    static const std::size_t N = 100;
+    long hashed [N] = { 0 };
+
+    std::size_t k;
+    for (k = 1; k != N && next; ++k) {
+        // generate a unique string
+        gen_str (s, k);
+
+        // compute hash value
+        hashed [k] = co.hash (s, s + std::char_traits<charT>::length(s));
+    }
+
+    // sort hashed values, then remove all duplicates
+    std::sort (hashed, hashed + k);
+    k = std::unique (hashed, hashed + k) - hashed;
+
+    // assert that the probability of a collision is less than 1%
+    // according to 22.2.4.1, p3, the likelihood should be very small,
+    // approaching 1.0 / numeric_limits<unsigned long>::max()
+    if (N - k > N /100) {
+        nfail++;
+        rw_assert (false, __FILE__, __LINE__,
+                   "collate<%s>::do_hash (const char_type*, "
+                   "const char_type*); "
+                   "probability of collision %f",
+                   charTname, double (N - k) / N);
+    }
+
+    rw_assert (!nfail, __FILE__, __LINE__,
+               "collate<%s>::do_hash () failed %d times", charTname,
+               nfail);
+
+}
+
+/**************************************************************************/
+
+
+template <class charT>
+/*static*/ void
+check_NUL (const char* charTname)
+{
+    rw_info (0, __FILE__, __LINE__,
+             "std::collate<%s>::compare() with embedded NULs", charTname);
+
+    // verify that the collate facet correctly handles
+    // character sequences with embedded NULs
+
+    charT buf_1 [STR_SIZE];
+    charT buf_2 [STR_SIZE];
+
+    bool fail = false;
+
+    unsigned i = 0;
+
+    for (const char* locname = rw_locales (LC_COLLATE);
+         *locname && !fail; locname += std::strlen (locname) + 1, ++i) {
+
+        std::locale loc;
+
+        _TRY {
+            loc = std::locale (locname);
+        }
+        _CATCH (...) {
+            continue;
+        }
+
+        const std::size_t buflen = sizeof buf_1 / sizeof *buf_1 - 1;
+
+        gen_str (buf_1, sizeof buf_1 / sizeof *buf_1);
+        std::memcpy (buf_2, buf_1, sizeof buf_2);
+
+        // compute a random index into the character buffers
+        // at which to set the element to NUL; the indices
+        // are such that (inx_1 > inx_2) always holds
+        const std::size_t inx_2 = std::rand () % (buflen - 1);
+        const std::size_t inx_1 =
+            inx_2 + 1 + std::rand () % (buflen - inx_2 - 1);
+
+        buf_2 [inx_2] = charT ();
+
+        typedef std::collate<charT> CollateT;
+
+        const CollateT &col = std::use_facet<CollateT>(loc);
+
+        int cmp = col.compare (buf_1, buf_1 + buflen, buf_2, buf_2 + buflen);
+
+        if (!cmp) {
+            typedef typename CollateT::string_type StringT;
+
+            const StringT str_1 (buf_1, buflen);
+            const StringT str_2 (buf_2, buflen);
+
+            fail = true;
+
+            rw_assert (false, __FILE__, __LINE__,
+                       "collate<%s>::compare(%{S}, ..., %{S}, ...) "
+                       "!= 0, got 0 in locale(\"%s\")", charTname,
+                       &str_1, &str_2, locname);
+        }
+
+        // set the character at the smaller index in both buffers to
+        // NUL, then set a character at the larger index in the first
+        // buffer to NUL, compare the two, and verify that the buffers
+        // compare unequal (buf_1 probably less)
+        buf_1 [inx_1] = charT ();
+        buf_1 [inx_2] = charT ();
+
+        cmp = col.compare (buf_1, buf_1 + buflen, buf_2, buf_2 + buflen);
+
+        if (!cmp) {
+            typedef typename CollateT::string_type StringT;
+
+            const StringT str_1 (buf_1, buflen);
+            const StringT str_2 (buf_2, buflen);
+
+            fail = true;
+
+            rw_assert (false, __FILE__, __LINE__,
+                       "collate<%s>::compare(%{S}, ..., %{S}, ...) "
+                       "!= 0, got 0 in locale(\"%s\")", charTname,
+                       &str_1, &str_2, locname);
+        }
+    }
+}
+
+/**************************************************************************/
+
+template <class charT>
+/*static*/ void
+do_test (const char* charTname)
+{
+    check_libstd_test_locale<charT> (charTname);
+    check_libstd<charT> (charTname);
+    check_libc<charT> (charTname);
+    check_NUL<charT> (charTname);
+    check_hash_eff<charT> (charTname);
+}
+
+
+#if _RWSTD_PATH_SEP == '/'
+#  define RM_RF    "rm -rf "
+#else
+#  define RM_RF    "rmdir /Q /S "
+#endif   // _RWSTD_PATH_SEP == '/'
+
+
+static int
+run_test (int /*argc*/, char* /*argv*/ [])
+{
+    // set any additional environment variables defined in
+    // the RW_PUTENV environment variable (if it exists)
+    rw_putenv (0);
+
+    // create a temporary directory for files created by the test
+    char namebuf [L_tmpnam];
+    locale_root = std::tmpnam (namebuf);
+
+    char envvar [sizeof LOCALE_ROOT + L_tmpnam] = LOCALE_ROOT "=";
+    std::strcat (envvar, locale_root);
+
+    rw_system ("mkdir %s", locale_root);
+
+    // set the LOCALE_ROOT variable where std::locale looks
+    // for locale database files
+    rw_putenv (envvar);
+
+    do_test<char> ("char");
+
+#ifndef _RWSTD_NO_WCHAR_T
+
+    do_test<wchar_t> ("wchar_t");
+
+#endif   // _RWSTD_NO_WCHAR_T
+
+    // remove temporary locale databases created by the test
+    rw_system (RM_RF "%s", locale_root);
+
+    return 0;
+}
+
+
+/*extern*/ int
+main (int argc, char* argv [])
+{
+    return rw_test (argc, argv, __FILE__,
+                    "[lib.category.collate]",
+                    "22.2.4 The collate category",
+                    run_test, "", 0);
+}
+



Reply via email to