LCOV - code coverage report
Current view: top level - tests - test_as2js_string.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1190 1210 98.3 %
Date: 2014-11-22 Functions: 14 15 93.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* test_as2js_string.cpp -- written by Alexis WILKE for Made to Order Software Corp. (c) 2005-2014 */
       2             : 
       3             : /*
       4             : 
       5             : Copyright (c) 2005-2014 Made to Order Software Corp.
       6             : 
       7             : http://snapwebsites.org/project/as2js
       8             : 
       9             : Permission is hereby granted, free of charge, to any
      10             : person obtaining a copy of this software and
      11             : associated documentation files (the "Software"), to
      12             : deal in the Software without restriction, including
      13             : without limitation the rights to use, copy, modify,
      14             : merge, publish, distribute, sublicense, and/or sell
      15             : copies of the Software, and to permit persons to whom
      16             : the Software is furnished to do so, subject to the
      17             : following conditions:
      18             : 
      19             : The above copyright notice and this permission notice
      20             : shall be included in all copies or substantial
      21             : portions of the Software.
      22             : 
      23             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
      24             : ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
      25             : LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
      26             : FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
      27             : EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
      28             : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      29             : WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
      30             : ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      31             : SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      32             : SOFTWARE.
      33             : 
      34             : */
      35             : 
      36             : #include    "test_as2js_string.h"
      37             : #include    "test_as2js_main.h"
      38             : 
      39             : #include    "as2js/string.h"
      40             : #include    "as2js/exceptions.h"
      41             : 
      42             : #include    <cstring>
      43             : #include    <algorithm>
      44             : 
      45             : #include    <cppunit/config/SourcePrefix.h>
      46           4 : CPPUNIT_TEST_SUITE_REGISTRATION( As2JsStringUnitTests );
      47             : 
      48             : namespace
      49             : {
      50             : #pragma GCC diagnostic push
      51             : #pragma GCC diagnostic ignored "-Wnarrowing"
      52             : #pragma GCC diagnostic ignored "-Woverflow"
      53             : char const iso8859_1_bad_start[] = { 0xA0, 0xA1, 0xA2, 0 };
      54             : char const iso8859_1_bom_and_bad_start[] = { 0xEF, 0xBB, 0xBF, 0xA0, 0xA1, 0xA2, 0 };
      55             : #pragma GCC diagnostic pop
      56             : wchar_t const utf16_to_append[] = { 0x1111, 0x2222, 0x3333, 0 };
      57             : as2js::as_char_t const utf32_to_append[] = { 0x101111, 0x5555, 0x103333, 0 };
      58             : 
      59             : 
      60        3203 : bool compare_chars(char const *a, as2js::as_char_t const *b)
      61             : {
      62    82546247 :     for(; *a != '\0' && *b != '\0'; ++a, ++b)
      63             :     {
      64    82543044 :         if(static_cast<unsigned char>(*a) != *b)
      65             :         {
      66           0 :             return false;
      67             :         }
      68             :     }
      69             : 
      70        3203 :     return static_cast<unsigned char>(*a) == *b;
      71             : }
      72             : 
      73     3024372 : int wctombs(char *mb, uint32_t wc)
      74             : {
      75     3024372 :     if(wc < 0x80)
      76             :     {
      77             :         /* this will also encode '\0'... */
      78      822073 :         mb[0] = static_cast<char>(wc);
      79      822073 :         mb[1] = '\0';
      80      822073 :         return 1;
      81             :     }
      82     2202299 :     if(wc < 0x800)
      83             :     {
      84      828576 :         mb[0] = static_cast<char>((wc >> 6) | 0xC0);
      85      828576 :         mb[1] = (wc & 0x3F) | 0x80;
      86      828576 :         mb[2] = '\0';
      87      828576 :         return 2;
      88             :     }
      89     1373723 :     if(wc < 0x10000)
      90             :     {
      91       63490 :         mb[0] = static_cast<char>((wc >> 12) | 0xE0);
      92       63490 :         mb[1] = ((wc >> 6) & 0x3F) | 0x80;
      93       63490 :         mb[2] = (wc & 0x3F) | 0x80;
      94       63490 :         mb[3] = '\0';
      95       63490 :         return 3;
      96             :     }
      97     1310233 :     if(wc < 0x200000)
      98             :     {
      99     1048745 :         mb[0] = static_cast<char>((wc >> 18) | 0xF0);
     100     1048745 :         mb[1] = ((wc >> 12) & 0x3F) | 0x80;
     101     1048745 :         mb[2] = ((wc >> 6) & 0x3F) | 0x80;
     102     1048745 :         mb[3] = (wc & 0x3F) | 0x80;
     103     1048745 :         mb[4] = '\0';
     104     1048745 :         return 4;
     105             :     }
     106      261488 :     if(wc < 0x4000000)
     107             :     {
     108        7860 :         mb[0] = (wc >> 24) | 0xF8;
     109        7860 :         mb[1] = ((wc >> 18) & 0x3F) | 0x80;
     110        7860 :         mb[2] = ((wc >> 12) & 0x3F) | 0x80;
     111        7860 :         mb[3] = ((wc >> 6) & 0x3F) | 0x80;
     112        7860 :         mb[4] = (wc & 0x3F) | 0x80;
     113        7860 :         mb[5] = '\0';
     114        7860 :         return 5;
     115             :     }
     116      253628 :     if(wc > 0)    // <=> (unsigned long) wc < 0x80000000
     117             :     {
     118      253628 :         mb[0] = (wc >> 30) | 0xFC;
     119      253628 :         mb[1] = ((wc >> 24) & 0x3F) | 0x80;
     120      253628 :         mb[2] = ((wc >> 18) & 0x3F) | 0x80;
     121      253628 :         mb[3] = ((wc >> 12) & 0x3F) | 0x80;
     122      253628 :         mb[4] = ((wc >> 6) & 0x3F) | 0x80;
     123      253628 :         mb[5] = (wc & 0x3F) | 0x80;
     124      253628 :         mb[6] = '\0';
     125      253628 :         return 6;
     126             :     }
     127             : 
     128             :     /* an invalid wide character (negative!) simply not encoded */
     129           0 :     mb[0] = '\0';
     130           0 :     return 0;
     131             : }
     132             : 
     133     1112123 : std::string wcstombs(as2js::String const& wcs)
     134             : {
     135     1112123 :     std::string mbs;
     136             : 
     137     3872852 :     for(as2js::as_char_t const *s(wcs.c_str()); *s != '\0'; ++s)
     138             :     {
     139             :         char buf[8];
     140     2760729 :         wctombs(buf, *s);
     141     2760729 :         mbs += buf;
     142             :     }
     143             : 
     144     1112123 :     return mbs;
     145             : }
     146             : 
     147         985 : int mbstowc(uint32_t& wc, char const *& mb, size_t& len)
     148             : {
     149             :     // define a default output character of NUL
     150         985 :     wc = L'\0';
     151             : 
     152             :     // done?
     153         985 :     if(len <= 0)
     154             :     {
     155          54 :         return 0;
     156             :     }
     157             : 
     158             :     // we eat one character from the source minimum
     159         931 :     unsigned char c(*mb++);
     160         931 :     len--;
     161             : 
     162         931 :     if(c < 0x80)
     163             :     {
     164          13 :         wc = c;
     165          13 :         return 1;
     166             :     }
     167             : 
     168             :     // invalid stream?
     169         918 :     if((c >= 0x80 && c <= 0xBF) || c == 0xFE || c == 0xFF)
     170             :     {
     171             :         // this is bad UTF-8, skip all the invalid bytes
     172          10 :         while(len > 0 && (c = *mb, (c >= 0x80 && c < 0xBF) || c == 0xFE || c == 0xFF))
     173             :         {
     174           2 :             mb++;
     175           2 :             len--;
     176             :         }
     177           4 :         return -2;
     178             :     }
     179             : 
     180             :     // use a uint32_t because some wchar_t are not wide
     181             :     // enough; generate an error later if that's the case
     182             :     // (we are trying to go to UCS-4, not UTF-16, but MS-Windows
     183             :     // really only supports UCS-2.)
     184         914 :     uint32_t w(L'\0');
     185             :     size_t cnt;
     186             : 
     187             :     // note that in current versions of UTF-8 0xFC and 0xF8
     188             :     // are not considered valid because they accept a maximum
     189             :     // of 20 bites instead of 31
     190         914 :     if(c >= 0xFC)
     191             :     {
     192           0 :         w = c & 0x01;
     193           0 :         cnt = 5;
     194             :     }
     195         914 :     else if(c >= 0xF8)
     196             :     {
     197           2 :         w = c & 0x03;
     198           2 :         cnt = 4;
     199             :     }
     200         912 :     else if(c >= 0xF0)
     201             :     {
     202         906 :         w = c & 0x07;
     203         906 :         cnt = 3;
     204             :     }
     205           6 :     else if(c >= 0xE0)
     206             :     {
     207           5 :         w = c & 0x0F;
     208           5 :         cnt = 2;
     209             :     }
     210           1 :     else if(c >= 0xC0)
     211             :     {
     212           1 :         w = c & 0x1F;
     213           1 :         cnt = 1;
     214             :     }
     215             :     else
     216             :     {
     217           0 :         throw std::logic_error("c < 0xC0 when it should not be");
     218             :     }
     219             : 
     220             :     // enough data in the input? if not, that's an error
     221         914 :     if(len < cnt)
     222             :     {
     223         568 :         while(len > 0 && (c = *mb, c >= 0x80 && c <= 0xBF))
     224             :         {
     225         188 :             len--;
     226         188 :             mb++;
     227             :         }
     228         190 :         return -1;
     229             :     }
     230         724 :     len -= cnt;
     231             : 
     232        2883 :     for(size_t l(cnt); l > 0; --l, mb++)
     233             :     {
     234        2164 :         c = *mb;
     235        2164 :         if(c < 0x80 || c > 0xBF)
     236             :         {
     237             :             // we got an invalid sequence!
     238             :             // restore whatever is left in len
     239           5 :             len += l;
     240           5 :             return -3;
     241             :         }
     242        2159 :         w = (w << 6) | (c & 0x3F);
     243             :     }
     244             : 
     245         719 :     wc = w;
     246             : 
     247         719 :     return static_cast<int>(cnt + 1);
     248             : }
     249             : 
     250           0 : as2js::String mbstowcs(std::string const& mbs)
     251             : {
     252           0 :     as2js::String wcs;
     253             : 
     254           0 :     size_t len(mbs.length());
     255           0 :     char const *s(mbs.c_str());
     256           0 :     while(*s != '\0')
     257             :     {
     258             :         uint32_t wc;
     259           0 :         int const l(mbstowc(wc, s, len));
     260           0 :         if(l > 0)
     261             :         {
     262           0 :             wcs += static_cast<as2js::as_char_t>(wc);
     263             :         }
     264             :     }
     265             : 
     266           0 :     return wcs;
     267             : }
     268             : 
     269       10191 : bool close_double(double a, double b, double epsilon)
     270             : {
     271       10191 :     return a >= b - epsilon && a <= b + epsilon;
     272             : }
     273             : 
     274             : }
     275             : // no name namespace
     276             : 
     277             : 
     278             : 
     279             : 
     280           1 : void As2JsStringUnitTests::test_iso88591()
     281             : {
     282             :     // to know whether code checks for UTF-8 we should provide
     283             :     // invalid input
     284             : 
     285             :     // a little extra test, make sure a string is empty on
     286             :     // creation without anything
     287             :     {
     288           1 :         as2js::String str1;
     289           1 :         CPPUNIT_ASSERT(str1.empty());
     290           1 :         CPPUNIT_ASSERT(str1.length() == 0);
     291           1 :         CPPUNIT_ASSERT(str1.utf8_length() == 0);
     292           1 :         CPPUNIT_ASSERT("" == str1);
     293           1 :         CPPUNIT_ASSERT(str1 == "");
     294           1 :         CPPUNIT_ASSERT(!("" != str1));
     295           1 :         CPPUNIT_ASSERT(!(str1 != ""));
     296           1 :         CPPUNIT_ASSERT(str1.valid());
     297             : 
     298           2 :         as2js::String str2("");
     299           1 :         CPPUNIT_ASSERT(str2.empty());
     300           1 :         CPPUNIT_ASSERT(str2.length() == 0);
     301           1 :         CPPUNIT_ASSERT(str2.utf8_length() == 0);
     302           1 :         CPPUNIT_ASSERT("" == str2);
     303           1 :         CPPUNIT_ASSERT(str2 == "");
     304           1 :         CPPUNIT_ASSERT(!("" != str2));
     305           1 :         CPPUNIT_ASSERT(!(str2 != ""));
     306           1 :         CPPUNIT_ASSERT(str2.valid());
     307             : 
     308           2 :         as2js::String str3(str1); // and a copy
     309           1 :         CPPUNIT_ASSERT(str3.empty());
     310           1 :         CPPUNIT_ASSERT(str3.length() == 0);
     311           1 :         CPPUNIT_ASSERT(str3.utf8_length() == 0);
     312           1 :         CPPUNIT_ASSERT("" == str3);
     313           1 :         CPPUNIT_ASSERT(str3 == "");
     314           1 :         CPPUNIT_ASSERT(!("" != str3));
     315           1 :         CPPUNIT_ASSERT(!(str3 != ""));
     316           1 :         CPPUNIT_ASSERT(str3.valid());
     317             : 
     318           2 :         std::string std_empty;
     319           2 :         as2js::String str4(std_empty); // and a copy from an std::string
     320           1 :         CPPUNIT_ASSERT(str4.empty());
     321           1 :         CPPUNIT_ASSERT(str4.length() == 0);
     322           1 :         CPPUNIT_ASSERT(str4.utf8_length() == 0);
     323           1 :         CPPUNIT_ASSERT("" == str4);
     324           1 :         CPPUNIT_ASSERT(str4 == "");
     325           1 :         CPPUNIT_ASSERT(!("" != str4));
     326           1 :         CPPUNIT_ASSERT(!(str4 != ""));
     327           1 :         CPPUNIT_ASSERT(str4.valid());
     328             : 
     329           2 :         as2js::String str5;
     330           1 :         CPPUNIT_ASSERT(str5.from_char("ignored", 0) == as2js::String::conversion_result_t::STRING_GOOD);
     331           1 :         CPPUNIT_ASSERT(str5.empty());
     332           1 :         CPPUNIT_ASSERT(str5.length() == 0);
     333           1 :         CPPUNIT_ASSERT(str5.utf8_length() == 0);
     334           1 :         CPPUNIT_ASSERT("" == str5);
     335           1 :         CPPUNIT_ASSERT(str5 == "");
     336           1 :         CPPUNIT_ASSERT(!("" != str5));
     337           1 :         CPPUNIT_ASSERT(!(str5 != ""));
     338           1 :         CPPUNIT_ASSERT(str5.valid());
     339             : 
     340           2 :         as2js::String str6;
     341           1 :         CPPUNIT_ASSERT(str6.from_char("", 5) == as2js::String::conversion_result_t::STRING_GOOD);
     342           1 :         CPPUNIT_ASSERT(str6.empty());
     343           1 :         CPPUNIT_ASSERT(str6.length() == 0);
     344           1 :         CPPUNIT_ASSERT(str6.utf8_length() == 0);
     345           1 :         CPPUNIT_ASSERT("" == str6);
     346           1 :         CPPUNIT_ASSERT(str6 == "");
     347           1 :         CPPUNIT_ASSERT(!("" != str6));
     348           1 :         CPPUNIT_ASSERT(!(str6 != ""));
     349           1 :         CPPUNIT_ASSERT(str6.valid());
     350             : 
     351           2 :         as2js::String str7;
     352           1 :         CPPUNIT_ASSERT(str7.from_char("") == as2js::String::conversion_result_t::STRING_GOOD);
     353           1 :         CPPUNIT_ASSERT(str7.empty());
     354           1 :         CPPUNIT_ASSERT(str7.length() == 0);
     355           1 :         CPPUNIT_ASSERT(str7.utf8_length() == 0);
     356           1 :         CPPUNIT_ASSERT("" == str7);
     357           1 :         CPPUNIT_ASSERT(str7 == "");
     358           1 :         CPPUNIT_ASSERT(!("" != str7));
     359           1 :         CPPUNIT_ASSERT(!(str7 != ""));
     360           1 :         CPPUNIT_ASSERT(str7.valid());
     361             : 
     362           2 :         as2js::String str8;
     363           1 :         CPPUNIT_ASSERT(&(str8 = "") == &str8);
     364           1 :         CPPUNIT_ASSERT(str8.empty());
     365           1 :         CPPUNIT_ASSERT(str8.length() == 0);
     366           1 :         CPPUNIT_ASSERT(str8.utf8_length() == 0);
     367           1 :         CPPUNIT_ASSERT("" == str8);
     368           1 :         CPPUNIT_ASSERT(str8 == "");
     369           1 :         CPPUNIT_ASSERT(!("" != str8));
     370           1 :         CPPUNIT_ASSERT(!(str8 != ""));
     371           1 :         CPPUNIT_ASSERT(str8.valid());
     372             : 
     373           1 :         char const *null_char_ptr(nullptr);
     374           2 :         as2js::String str9(null_char_ptr, 4);
     375           1 :         CPPUNIT_ASSERT(&(str9 = "") == &str9);
     376           1 :         CPPUNIT_ASSERT(str9.empty());
     377           1 :         CPPUNIT_ASSERT(str9.length() == 0);
     378           1 :         CPPUNIT_ASSERT(str9.utf8_length() == 0);
     379           1 :         CPPUNIT_ASSERT(!("" != str9));
     380           1 :         CPPUNIT_ASSERT(!(str9 != ""));
     381           1 :         CPPUNIT_ASSERT(str9.valid());
     382             : 
     383           2 :         as2js::String str10;
     384           1 :         str10.from_char(null_char_ptr, 6);
     385           1 :         CPPUNIT_ASSERT(&(str10 = "") == &str10);
     386           1 :         CPPUNIT_ASSERT(str10.empty());
     387           1 :         CPPUNIT_ASSERT(str10.length() == 0);
     388           1 :         CPPUNIT_ASSERT(str10.utf8_length() == 0);
     389           1 :         CPPUNIT_ASSERT("" == str10);
     390           1 :         CPPUNIT_ASSERT(str10 == "");
     391           1 :         CPPUNIT_ASSERT(!("" != str10));
     392           1 :         CPPUNIT_ASSERT(!(str10 != ""));
     393           2 :         CPPUNIT_ASSERT(str10.valid());
     394             :     }
     395             : 
     396             :     // characters between 0x80 and 0xBF are only to chain UTF-8
     397             :     // codes, so if they start a string and are accepted, we're
     398             :     // not in UTF-8
     399             :     {
     400             :         // constructor
     401           1 :         as2js::String str1(iso8859_1_bad_start);
     402           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bad_start) == str1.length());
     403           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bad_start, str1.c_str()));
     404           1 :         CPPUNIT_ASSERT(iso8859_1_bad_start == str1);
     405           1 :         CPPUNIT_ASSERT(!(iso8859_1_bad_start != str1));
     406           1 :         CPPUNIT_ASSERT(str1 == iso8859_1_bad_start);
     407           1 :         CPPUNIT_ASSERT(!(str1 != iso8859_1_bad_start));
     408           1 :         CPPUNIT_ASSERT(str1.valid());
     409             : 
     410             :         // then copy operator
     411           2 :         as2js::String str2(str1);
     412           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bad_start) == str2.length());
     413           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bad_start, str2.c_str()));
     414           1 :         CPPUNIT_ASSERT(iso8859_1_bad_start == str2);
     415           1 :         CPPUNIT_ASSERT(!(iso8859_1_bad_start != str2));
     416           1 :         CPPUNIT_ASSERT(str2 == iso8859_1_bad_start);
     417           1 :         CPPUNIT_ASSERT(!(str2 != iso8859_1_bad_start));
     418           1 :         CPPUNIT_ASSERT(str2.valid());
     419             : 
     420             :         // copy from std::string
     421           2 :         std::string std(iso8859_1_bad_start);
     422           2 :         as2js::String str3(std);
     423           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bad_start) == str3.length());
     424           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bad_start, str3.c_str()));
     425           1 :         CPPUNIT_ASSERT(iso8859_1_bad_start == str3);
     426           1 :         CPPUNIT_ASSERT(!(iso8859_1_bad_start != str3));
     427           1 :         CPPUNIT_ASSERT(str3 == iso8859_1_bad_start);
     428           1 :         CPPUNIT_ASSERT(!(str3 != iso8859_1_bad_start));
     429           1 :         CPPUNIT_ASSERT(str3.valid());
     430             : 
     431           2 :         as2js::String str4;
     432           1 :         CPPUNIT_ASSERT(str4.from_char(iso8859_1_bad_start) == as2js::String::conversion_result_t::STRING_GOOD);
     433           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bad_start) == str4.length());
     434           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bad_start, str4.c_str()));
     435           1 :         CPPUNIT_ASSERT(iso8859_1_bad_start == str4);
     436           1 :         CPPUNIT_ASSERT(!(iso8859_1_bad_start != str4));
     437           1 :         CPPUNIT_ASSERT(str4 == iso8859_1_bad_start);
     438           1 :         CPPUNIT_ASSERT(!(str4 != iso8859_1_bad_start));
     439           1 :         CPPUNIT_ASSERT(str4.valid());
     440             : 
     441           2 :         as2js::String str5;
     442           1 :         CPPUNIT_ASSERT(&(str5 = iso8859_1_bad_start) == &str5);
     443           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bad_start) == str5.length());
     444           1 :         CPPUNIT_ASSERT(iso8859_1_bad_start == str5);
     445           1 :         CPPUNIT_ASSERT(!(iso8859_1_bad_start != str5));
     446           1 :         CPPUNIT_ASSERT(str5 == iso8859_1_bad_start);
     447           1 :         CPPUNIT_ASSERT(!(str5 != iso8859_1_bad_start));
     448           2 :         CPPUNIT_ASSERT(str5.valid());
     449             :     }
     450             : 
     451             :     // make sure that the UTF-8 BOM does not change a thing
     452             :     {
     453             :         // constructor
     454           1 :         as2js::String str1(iso8859_1_bom_and_bad_start);
     455           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bom_and_bad_start) == str1.length());
     456           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bom_and_bad_start, str1.c_str()));
     457           1 :         CPPUNIT_ASSERT(iso8859_1_bom_and_bad_start == str1);
     458           1 :         CPPUNIT_ASSERT(!(iso8859_1_bom_and_bad_start != str1));
     459           1 :         CPPUNIT_ASSERT(str1 == iso8859_1_bom_and_bad_start);
     460           1 :         CPPUNIT_ASSERT(!(str1 != iso8859_1_bom_and_bad_start));
     461           1 :         CPPUNIT_ASSERT(str1.valid());
     462             : 
     463             :         // then copy operator
     464           2 :         as2js::String str2(str1);
     465           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bom_and_bad_start) == str2.length());
     466           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bom_and_bad_start, str2.c_str()));
     467           1 :         CPPUNIT_ASSERT(iso8859_1_bom_and_bad_start == str2);
     468           1 :         CPPUNIT_ASSERT(!(iso8859_1_bom_and_bad_start != str2));
     469           1 :         CPPUNIT_ASSERT(str2 == iso8859_1_bom_and_bad_start);
     470           1 :         CPPUNIT_ASSERT(!(str2 != iso8859_1_bom_and_bad_start));
     471           1 :         CPPUNIT_ASSERT(str2.valid());
     472             : 
     473             :         // copy from std::string
     474           2 :         std::string std(iso8859_1_bom_and_bad_start);
     475           2 :         as2js::String str3(std);
     476           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bom_and_bad_start) == str3.length());
     477           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bom_and_bad_start, str3.c_str()));
     478           1 :         CPPUNIT_ASSERT(iso8859_1_bom_and_bad_start == str3);
     479           1 :         CPPUNIT_ASSERT(!(iso8859_1_bom_and_bad_start != str3));
     480           1 :         CPPUNIT_ASSERT(str3 == iso8859_1_bom_and_bad_start);
     481           1 :         CPPUNIT_ASSERT(!(str3 != iso8859_1_bom_and_bad_start));
     482           1 :         CPPUNIT_ASSERT(str3.valid());
     483             : 
     484           2 :         as2js::String str4;
     485           1 :         CPPUNIT_ASSERT(str4.from_char(iso8859_1_bom_and_bad_start) == as2js::String::conversion_result_t::STRING_GOOD);
     486           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bom_and_bad_start) == str4.length());
     487           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bom_and_bad_start, str4.c_str()));
     488           1 :         CPPUNIT_ASSERT(iso8859_1_bom_and_bad_start == str4);
     489           1 :         CPPUNIT_ASSERT(!(str4 != iso8859_1_bom_and_bad_start));
     490           1 :         CPPUNIT_ASSERT(str4.valid());
     491             : 
     492           2 :         as2js::String str5;
     493           1 :         CPPUNIT_ASSERT(&(str5 = iso8859_1_bom_and_bad_start) == &str5);
     494           1 :         CPPUNIT_ASSERT(strlen(iso8859_1_bom_and_bad_start) == str5.length());
     495           1 :         CPPUNIT_ASSERT(compare_chars(iso8859_1_bom_and_bad_start, str5.c_str()));
     496           1 :         CPPUNIT_ASSERT(iso8859_1_bom_and_bad_start == str5);
     497           1 :         CPPUNIT_ASSERT(!(iso8859_1_bom_and_bad_start != str5));
     498           1 :         CPPUNIT_ASSERT(str5 == iso8859_1_bom_and_bad_start);
     499           1 :         CPPUNIT_ASSERT(!(str5 != iso8859_1_bom_and_bad_start));
     500           2 :         CPPUNIT_ASSERT(str5.valid());
     501             :     }
     502             : 
     503             :     // try with all possible bytes now, the order would totally break
     504             :     // UTF-8 in many places
     505             :     {
     506             :         char buf[256];
     507         256 :         for(int i(0); i < 255; ++i)
     508             :         {
     509         255 :             buf[i] = static_cast<char>(i + 1);
     510             :         }
     511           1 :         buf[255] = '\0';
     512             : 
     513             :         // constructor
     514           1 :         as2js::String str1(buf);
     515           1 :         CPPUNIT_ASSERT(strlen(buf) == str1.length());
     516           1 :         CPPUNIT_ASSERT(compare_chars(buf, str1.c_str()));
     517           1 :         CPPUNIT_ASSERT(buf == str1);
     518           1 :         CPPUNIT_ASSERT(str1 == buf);
     519           1 :         CPPUNIT_ASSERT(!(buf != str1));
     520           1 :         CPPUNIT_ASSERT(!(str1 != buf));
     521           1 :         CPPUNIT_ASSERT(str1.valid());
     522             : 
     523             :         // then copy operator
     524           2 :         as2js::String str2(str1);
     525           1 :         CPPUNIT_ASSERT(strlen(buf) == str2.length());
     526           1 :         CPPUNIT_ASSERT(compare_chars(buf, str1.c_str()));
     527           1 :         CPPUNIT_ASSERT(buf == str2);
     528           1 :         CPPUNIT_ASSERT(str2 == buf);
     529           1 :         CPPUNIT_ASSERT(!(buf != str2));
     530           1 :         CPPUNIT_ASSERT(!(str2 != buf));
     531           1 :         CPPUNIT_ASSERT(str2.valid());
     532             : 
     533             :         // copy from std::string
     534           2 :         std::string std(buf);
     535           2 :         as2js::String str3(std);
     536           1 :         CPPUNIT_ASSERT(strlen(buf) == str3.length());
     537           1 :         CPPUNIT_ASSERT(compare_chars(buf, str3.c_str()));
     538           1 :         CPPUNIT_ASSERT(buf == str3);
     539           1 :         CPPUNIT_ASSERT(str3 == buf);
     540           1 :         CPPUNIT_ASSERT(!(buf != str3));
     541           1 :         CPPUNIT_ASSERT(!(str3 != buf));
     542           1 :         CPPUNIT_ASSERT(str3.valid());
     543             : 
     544           2 :         as2js::String str4;
     545           1 :         CPPUNIT_ASSERT(str4.from_char(buf) == as2js::String::conversion_result_t::STRING_GOOD);
     546           1 :         CPPUNIT_ASSERT(strlen(buf) == str4.length());
     547           1 :         CPPUNIT_ASSERT(compare_chars(buf, str4.c_str()));
     548           1 :         CPPUNIT_ASSERT(buf == str4);
     549           1 :         CPPUNIT_ASSERT(str4 == buf);
     550           1 :         CPPUNIT_ASSERT(!(buf != str4));
     551           1 :         CPPUNIT_ASSERT(!(str4 != buf));
     552           2 :         CPPUNIT_ASSERT(str4.valid());
     553             :     }
     554             : 
     555             :     // try with random strings
     556             :     {
     557             :         char buf[64 * 1024];
     558             : 
     559          51 :         for(int i(0); i < 50; ++i)
     560             :         {
     561          50 :             if(!as2js_test::g_gui && i % 5 == 4)
     562             :             {
     563          10 :                 std::cout << "." << std::flush;
     564             :             }
     565             : 
     566          50 :             size_t const max_size(rand() % (sizeof(buf) - 5));
     567     1648652 :             for(size_t j(0); j < max_size; ++j)
     568             :             {
     569             :                 // generate a number from 1 to 255
     570             :                 // (we do not support '\0' in our strings
     571     1655028 :                 do
     572             :                 {
     573     1655028 :                     buf[j] = static_cast<char>(rand());
     574             :                 }
     575     1655028 :                 while(buf[j] == '\0');
     576             :             }
     577          50 :             buf[max_size] = '\0';
     578          50 :             CPPUNIT_ASSERT(strlen(buf) == max_size); // just in case
     579             : 
     580             :             // constructor
     581          50 :             as2js::String str1(buf);
     582          50 :             CPPUNIT_ASSERT(strlen(buf) == str1.length());
     583          50 :             CPPUNIT_ASSERT(compare_chars(buf, str1.c_str()));
     584          50 :             CPPUNIT_ASSERT(buf == str1);
     585          50 :             CPPUNIT_ASSERT(str1 == buf);
     586          50 :             CPPUNIT_ASSERT(!(buf != str1));
     587          50 :             CPPUNIT_ASSERT(!(str1 != buf));
     588          50 :             CPPUNIT_ASSERT(str1.valid());
     589             : 
     590             :             {
     591          50 :                 std::stringstream ss;
     592          50 :                 ss << str1;
     593         100 :                 as2js::String wcs(buf); // this is verified in different places
     594         100 :                 std::string utf8(wcstombs(wcs));
     595         100 :                 CPPUNIT_ASSERT(ss.str() == utf8);
     596             :             }
     597             : 
     598             :             // then copy operator
     599         100 :             as2js::String str2(str1);
     600          50 :             CPPUNIT_ASSERT(strlen(buf) == str2.length());
     601          50 :             CPPUNIT_ASSERT(compare_chars(buf, str1.c_str()));
     602          50 :             CPPUNIT_ASSERT(buf == str2);
     603          50 :             CPPUNIT_ASSERT(str2 == buf);
     604          50 :             CPPUNIT_ASSERT(!(buf != str2));
     605          50 :             CPPUNIT_ASSERT(!(str2 != buf));
     606          50 :             CPPUNIT_ASSERT(str2.valid());
     607             : 
     608             :             // copy from std::string
     609         100 :             std::string std(buf);
     610         100 :             as2js::String str3(std);
     611          50 :             CPPUNIT_ASSERT(strlen(buf) == str3.length());
     612          50 :             CPPUNIT_ASSERT(compare_chars(buf, str3.c_str()));
     613          50 :             CPPUNIT_ASSERT(buf == str3);
     614          50 :             CPPUNIT_ASSERT(str3 == buf);
     615          50 :             CPPUNIT_ASSERT(!(buf != str3));
     616          50 :             CPPUNIT_ASSERT(!(str3 != buf));
     617          50 :             CPPUNIT_ASSERT(str3.valid());
     618             : 
     619             :             // also test the from_char(), should get the same result
     620         100 :             as2js::String str4;
     621          50 :             CPPUNIT_ASSERT(str4.from_char(buf) == as2js::String::conversion_result_t::STRING_GOOD);
     622          50 :             CPPUNIT_ASSERT(strlen(buf) == str4.length());
     623          50 :             CPPUNIT_ASSERT(compare_chars(buf, str4.c_str()));
     624          50 :             CPPUNIT_ASSERT(buf == str4);
     625          50 :             CPPUNIT_ASSERT(str4 == buf);
     626          50 :             CPPUNIT_ASSERT(!(buf != str4));
     627          50 :             CPPUNIT_ASSERT(!(str4 != buf));
     628          50 :             CPPUNIT_ASSERT(str4.valid());
     629             : 
     630             :             // also test the from_char(), should get the same result
     631         100 :             as2js::String str5;
     632          50 :             CPPUNIT_ASSERT(&(str5 = std) == &str5);
     633          50 :             CPPUNIT_ASSERT(strlen(buf) == str5.length());
     634          50 :             CPPUNIT_ASSERT(compare_chars(buf, str5.c_str()));
     635          50 :             CPPUNIT_ASSERT(buf == str5);
     636          50 :             CPPUNIT_ASSERT(str5 == buf);
     637          50 :             CPPUNIT_ASSERT(!(buf != str5));
     638          50 :             CPPUNIT_ASSERT(!(str5 != buf));
     639          50 :             CPPUNIT_ASSERT(str5.valid());
     640             : 
     641             :             // try truncation the input string
     642             :             // note: copy operators do not offer a truncate capability
     643        1050 :             for(int k(0); k < 20; ++k)
     644             :             {
     645        1000 :                 size_t const size(rand() % (max_size * 2));
     646        1000 :                 size_t const end(std::min(size, strlen(buf)));
     647             : 
     648             :                 // constructor
     649        1000 :                 as2js::String str1_1(buf, size);
     650        1000 :                 CPPUNIT_ASSERT(end == str1_1.length());
     651        1000 :                 char save1_1(buf[end]);
     652        1000 :                 buf[end] = '\0';
     653        1000 :                 CPPUNIT_ASSERT(compare_chars(buf, str1_1.c_str()));
     654        1000 :                 CPPUNIT_ASSERT(buf == str1_1);
     655        1000 :                 CPPUNIT_ASSERT(str1_1 == buf);
     656        1000 :                 CPPUNIT_ASSERT(!(buf != str1_1));
     657        1000 :                 CPPUNIT_ASSERT(!(str1_1 != buf));
     658        1000 :                 buf[end] = save1_1;
     659        1000 :                 CPPUNIT_ASSERT(str1_1.valid());
     660             : 
     661        2000 :                 as2js::String str1_2;
     662        1000 :                 CPPUNIT_ASSERT(str1_2.from_char(buf, size) == as2js::String::conversion_result_t::STRING_GOOD);
     663        1000 :                 char save1_2(buf[end]);
     664        1000 :                 buf[end] = '\0';
     665        1000 :                 CPPUNIT_ASSERT(strlen(buf) == str1_2.length());
     666        1000 :                 CPPUNIT_ASSERT(compare_chars(buf, str1_2.c_str()));
     667        1000 :                 CPPUNIT_ASSERT(buf == str1_2);
     668        1000 :                 CPPUNIT_ASSERT(str1_2 == buf);
     669        1000 :                 CPPUNIT_ASSERT(!(buf != str1_2));
     670        1000 :                 CPPUNIT_ASSERT(!(str1_2 != buf));
     671        1000 :                 buf[end] = save1_2;
     672        1000 :                 CPPUNIT_ASSERT(str1_2.valid());
     673        1000 :             }
     674             : 
     675             :             // now try a += char
     676         300 :             for(int k(0); k < 5; ++k)
     677             :             {
     678         250 :                 char random(rand());
     679         502 :                 while(random == '\0')
     680             :                 {
     681           2 :                     random = rand();
     682             :                 }
     683         250 :                 size_t l(strlen(buf));
     684         250 :                 buf[l] = random; // we have at least 10 bytes extra for this purpose
     685         250 :                 buf[l + 1] = '\0';
     686         250 :                 CPPUNIT_ASSERT(&(str1 += random) == &str1);
     687         250 :                 CPPUNIT_ASSERT(strlen(buf) == str1.length());
     688         250 :                 CPPUNIT_ASSERT(compare_chars(buf, str1.c_str()));
     689         250 :                 CPPUNIT_ASSERT(buf == str1);
     690         250 :                 CPPUNIT_ASSERT(str1 == buf);
     691         250 :                 CPPUNIT_ASSERT(!(buf != str1));
     692         250 :                 CPPUNIT_ASSERT(!(str1 != buf));
     693         250 :                 CPPUNIT_ASSERT(str1.valid());
     694             : 
     695             :                 char buf2_2[64 * 1024 + 10];
     696         250 :                 strcpy(buf2_2, "foo: ");
     697         250 :                 strcat(buf2_2, buf);
     698         250 :                 as2js::String str2_2("foo: ");
     699         250 :                 CPPUNIT_ASSERT(&(str2_2 += buf) == &str2_2);
     700         250 :                 CPPUNIT_ASSERT(strlen(buf) + 5 == str2_2.length());
     701         250 :                 CPPUNIT_ASSERT(strlen(buf2_2) == str2_2.length());
     702         250 :                 CPPUNIT_ASSERT(compare_chars(buf2_2, str2_2.c_str()));
     703         250 :                 CPPUNIT_ASSERT(buf2_2 == str2_2);
     704         250 :                 CPPUNIT_ASSERT(str2_2 == buf2_2);
     705         250 :                 CPPUNIT_ASSERT(!(buf2_2 != str2_2));
     706         250 :                 CPPUNIT_ASSERT(!(str2_2 != buf2_2));
     707         250 :                 CPPUNIT_ASSERT(str2_2.valid());
     708             : 
     709         500 :                 as2js::String str2_3("foo: ");
     710         500 :                 std::string lstd(buf);
     711         250 :                 CPPUNIT_ASSERT(&(str2_3 += lstd) == &str2_3);
     712         250 :                 CPPUNIT_ASSERT(strlen(buf) + 5 == str2_3.length());
     713         250 :                 CPPUNIT_ASSERT(strlen(buf2_2) == str2_3.length());
     714         250 :                 CPPUNIT_ASSERT(compare_chars(buf2_2, str2_3.c_str()));
     715         250 :                 CPPUNIT_ASSERT(buf2_2 == str2_3);
     716         250 :                 CPPUNIT_ASSERT(str2_3 == buf2_2);
     717         250 :                 CPPUNIT_ASSERT(!(buf2_2 != str2_3));
     718         250 :                 CPPUNIT_ASSERT(!(str2_3 != buf2_2));
     719         250 :                 CPPUNIT_ASSERT(str2_3.valid());
     720         250 :             }
     721          50 :         }
     722             :     }
     723           1 : }
     724             : 
     725             : 
     726           1 : void As2JsStringUnitTests::test_utf8()
     727             : {
     728             :     // all the other contructor tests verify that they do not support
     729             :     // UTF-8; there are no UTF-8 constructors actually, so here all
     730             :     // we can test is the from_utf8().
     731             : 
     732             :     {
     733           1 :         char const *null_char_ptr(nullptr);
     734           1 :         as2js::String str1;
     735           1 :         str1.from_utf8(null_char_ptr, 3);
     736           1 :         CPPUNIT_ASSERT(&(str1 = "") == &str1);
     737           1 :         CPPUNIT_ASSERT(str1.empty());
     738           1 :         CPPUNIT_ASSERT(str1.length() == 0);
     739           1 :         CPPUNIT_ASSERT(str1.utf8_length() == 0);
     740           1 :         CPPUNIT_ASSERT("" == str1);
     741           1 :         CPPUNIT_ASSERT(str1 == "");
     742           1 :         CPPUNIT_ASSERT(!("" != str1));
     743           1 :         CPPUNIT_ASSERT(!(str1 != ""));
     744           1 :         CPPUNIT_ASSERT(str1.valid());
     745             :     }
     746             : 
     747             :     // first check a few small strings
     748          11 :     for(int i(0); i < 10; ++i)
     749             :     {
     750             :         // 5 to 9 character strings
     751             :         int32_t buf[10];
     752          10 :         size_t const max_chars(rand() % 5 + 5);
     753          74 :         for(size_t j(0); j < max_chars; ++j)
     754             :         {
     755          64 :             uint32_t wc(0);
     756         120 :             do
     757             :             {
     758         120 :                 wc = rand() & 0x001FFFFF;
     759             :             }
     760         120 :             while(wc == 0 || wc > 0x0010FFFF || (wc >= 0xD800 && wc <= 0xDFFF));
     761          64 :             CPPUNIT_ASSERT(as2js::String::valid_character(wc));
     762          64 :             buf[j] = wc;
     763             :         }
     764          10 :         buf[max_chars] = '\0';
     765          10 :         as2js::String wcs(buf); // testing UTF-32 here!
     766          20 :         std::string mbs(wcstombs(wcs));
     767             : 
     768             :         {
     769          10 :             as2js::String str1;
     770          10 :             CPPUNIT_ASSERT(str1.from_utf8(mbs.c_str()) == as2js::String::conversion_result_t::STRING_GOOD);
     771          10 :             CPPUNIT_ASSERT(max_chars == str1.length());
     772          10 :             CPPUNIT_ASSERT(buf == str1);
     773          10 :             CPPUNIT_ASSERT(str1 == buf);
     774          10 :             CPPUNIT_ASSERT(!(buf != str1));
     775          10 :             CPPUNIT_ASSERT(!(str1 != buf));
     776          10 :             CPPUNIT_ASSERT(str1.valid());
     777          10 :             CPPUNIT_ASSERT(str1.utf8_length() == static_cast<ssize_t>(mbs.length()));
     778          10 :             CPPUNIT_ASSERT(mbs == str1.to_utf8());
     779             : 
     780             :             // try copies of larger characters
     781          20 :             as2js::String str2(str1);
     782          10 :             CPPUNIT_ASSERT(max_chars == str2.length());
     783          10 :             CPPUNIT_ASSERT(buf == str2);
     784          10 :             CPPUNIT_ASSERT(str2 == buf);
     785          10 :             CPPUNIT_ASSERT(!(buf != str2));
     786          10 :             CPPUNIT_ASSERT(!(str2 != buf));
     787          10 :             CPPUNIT_ASSERT(str2.valid());
     788          10 :             CPPUNIT_ASSERT(str2.utf8_length() == static_cast<ssize_t>(mbs.length()));
     789          10 :             CPPUNIT_ASSERT(mbs == str2.to_utf8());
     790             : 
     791             :             // test with a size (but that can break the UTF-8 encoding so
     792             :             // we have to be careful...)
     793         254 :             for(size_t k(1); k < mbs.length(); ++k)
     794             :             {
     795             :                 // verify size
     796         244 :                 char const *sub(mbs.c_str());
     797         244 :                 size_t length(k);
     798             :                 uint32_t wc;
     799         244 :                 as2js::String out;
     800         244 :                 int r(0);
     801         963 :                 do
     802             :                 {
     803         963 :                     r = mbstowc(wc, sub, length);
     804         963 :                     if(wc > 0)
     805             :                     {
     806         719 :                         out += static_cast<as2js::as_char_t>(wc);
     807             :                     }
     808             :                 }
     809             :                 while(r > 0);
     810             :                 // all characters are good, but we may read the end early
     811             :                 as2js::String::conversion_result_t const cr(
     812             :                         r == 0 ? as2js::String::conversion_result_t::STRING_GOOD
     813             :                                : as2js::String::conversion_result_t::STRING_END
     814         244 :                     );
     815         488 :                 as2js::String str3;
     816         244 :                 CPPUNIT_ASSERT(str3.from_utf8(mbs.c_str(), k) == cr);
     817         244 :                 if(r == 0)
     818             :                 {
     819          54 :                     CPPUNIT_ASSERT(out.length() == str3.length());
     820          54 :                     CPPUNIT_ASSERT(out == str3);
     821          54 :                     CPPUNIT_ASSERT(str3 == out);
     822          54 :                     CPPUNIT_ASSERT(!(out != str3));
     823          54 :                     CPPUNIT_ASSERT(!(str3 != out));
     824          54 :                     CPPUNIT_ASSERT(str3.valid());
     825          54 :                     CPPUNIT_ASSERT(str3.utf8_length() == static_cast<ssize_t>(k));
     826          54 :                     CPPUNIT_ASSERT(mbs.substr(0, k) == str3.to_utf8());
     827             :                 }
     828             :                 else
     829             :                 {
     830             :                     // if an error occurs the destination remains unchanged
     831         190 :                     CPPUNIT_ASSERT(0 == str3.length());
     832         190 :                     CPPUNIT_ASSERT(compare_chars("", str3.c_str()));
     833         190 :                     CPPUNIT_ASSERT("" == str3);
     834         190 :                     CPPUNIT_ASSERT(str3 == "");
     835         190 :                     CPPUNIT_ASSERT(!("" != str3));
     836         190 :                     CPPUNIT_ASSERT(!(str3 != ""));
     837         190 :                     CPPUNIT_ASSERT(str3.valid());
     838         190 :                     CPPUNIT_ASSERT(str3.utf8_length() == 0);
     839         190 :                     CPPUNIT_ASSERT("" == str3.to_utf8());
     840             :                 }
     841         254 :             }
     842             :         }
     843          10 :     }
     844             : 
     845             :     // then check all the characters (Except '\0')
     846     1112065 :     for(int i(1); i < 0x110000; ++i)
     847             :     {
     848             :         // skip the UTF-16 surrogate which are not considered valid
     849             :         // UTF-8
     850     1112064 :         if(i == 0x00D800)
     851             :         {
     852           1 :             i = 0x00DFFF;
     853           1 :             continue;
     854             :         }
     855             : 
     856     1112063 :         CPPUNIT_ASSERT(as2js::String::valid_character(i));
     857             : 
     858     1112063 :         if(!as2js_test::g_gui && (i & 0x00FFFF) == 0)
     859             :         {
     860          16 :             std::cout << "." << std::flush;
     861             :         }
     862             : 
     863             :         int32_t buf[2];
     864     1112063 :         buf[0] = i;
     865     1112063 :         buf[1] = '\0';
     866     1112063 :         as2js::String wcs(buf); // testing UTF-32 here!
     867     2224126 :         std::string mbs(wcstombs(wcs));
     868             : 
     869             :         {
     870     1112063 :             as2js::String str1;
     871     1112063 :             CPPUNIT_ASSERT(str1.from_utf8(mbs.c_str()) == as2js::String::conversion_result_t::STRING_GOOD);
     872     1112063 :             CPPUNIT_ASSERT(1 == str1.length());
     873     1112063 :             CPPUNIT_ASSERT(buf == str1);
     874     1112063 :             CPPUNIT_ASSERT(str1 == buf);
     875     1112063 :             CPPUNIT_ASSERT(!(buf != str1));
     876     1112063 :             CPPUNIT_ASSERT(!(str1 != buf));
     877     1112063 :             CPPUNIT_ASSERT(str1.valid());
     878     1112063 :             CPPUNIT_ASSERT(str1.utf8_length() == static_cast<ssize_t>(mbs.length()));
     879     1112063 :             CPPUNIT_ASSERT(mbs == str1.to_utf8());
     880             : 
     881             :             // try copies of larger characters
     882     2224126 :             as2js::String str2(str1);
     883     1112063 :             CPPUNIT_ASSERT(1 == str2.length());
     884     1112063 :             CPPUNIT_ASSERT(buf == str2);
     885     1112063 :             CPPUNIT_ASSERT(str2 == buf);
     886     1112063 :             CPPUNIT_ASSERT(!(buf != str2));
     887     1112063 :             CPPUNIT_ASSERT(!(str2 != buf));
     888     1112063 :             CPPUNIT_ASSERT(str2.valid());
     889     1112063 :             CPPUNIT_ASSERT(str2.utf8_length() == static_cast<ssize_t>(mbs.length()));
     890     2224126 :             CPPUNIT_ASSERT(mbs == str2.to_utf8());
     891             :         }
     892     1112063 :     }
     893             : 
     894             :     // test that the surrogate all crap out
     895        2049 :     for(int i(0xD800); i < 0xE000; ++i)
     896             :     {
     897        2048 :         CPPUNIT_ASSERT(!as2js::String::valid_character(i));
     898             : 
     899             :         // WARNING: cannot use the String to convert to wcs because
     900             :         //          that catches those invalid characters too!
     901             :         char buf[8];
     902        2048 :         wctombs(buf, i);
     903             : 
     904        2048 :         as2js::String str1;
     905        2048 :         CPPUNIT_ASSERT(str1.from_utf8(buf) == as2js::String::conversion_result_t::STRING_INVALID);
     906        2048 :         CPPUNIT_ASSERT(str1.empty()); // not modified
     907             : 
     908        4096 :         as2js::String str2("old value");
     909        2048 :         CPPUNIT_ASSERT(str2.from_utf8(buf) == as2js::String::conversion_result_t::STRING_INVALID);
     910        2048 :         CPPUNIT_ASSERT(str2.length() == 9); // not modified
     911        2048 :         CPPUNIT_ASSERT(str2 == "old value"); // not modified
     912        2048 :     }
     913             : 
     914             :     // now to test bad encoding, generate random data and make
     915             :     // sure we detect it as incorrect then call the String
     916             :     // implementation
     917             :     // first test the sequences we expect to be wrong by themselves
     918             :     {
     919             :         char buf[16];
     920          65 :         for(int i(0x80); i < 0xC0; ++i)
     921             :         {
     922          64 :             buf[0] = i;
     923          64 :             buf[1] = '?';
     924          64 :             buf[2] = '\0';
     925             : 
     926          64 :             as2js::String str1;
     927          64 :             CPPUNIT_ASSERT(str1.from_utf8(buf) == as2js::String::conversion_result_t::STRING_BAD);
     928          64 :         }
     929             :         {
     930           1 :             buf[0] = static_cast<char>(0xFE);
     931           1 :             buf[1] = '?';
     932           1 :             buf[2] = '\0';
     933             : 
     934           1 :             as2js::String str1;
     935           1 :             CPPUNIT_ASSERT(str1.from_utf8(buf) == as2js::String::conversion_result_t::STRING_BAD);
     936             :         }
     937             :         {
     938           1 :             buf[0] = static_cast<char>(0xFF);
     939           1 :             buf[1] = '?';
     940           1 :             buf[2] = '\0';
     941             : 
     942           1 :             as2js::String str1;
     943           1 :             CPPUNIT_ASSERT(str1.from_utf8(buf) == as2js::String::conversion_result_t::STRING_BAD);
     944             :         }
     945          62 :         for(int i(0xC0); i < 0xFD; ++i)
     946             :         {
     947             :             // valid introducer
     948          61 :             buf[0] = i;
     949          83 :             do
     950             :             {
     951             :                 // invalid continuation
     952          83 :                 buf[1] = rand();
     953             :             }
     954          83 :             while(static_cast<unsigned char>(buf[1]) == '\0'
     955          83 :                 || (static_cast<unsigned char>(buf[1]) >= 0x80 && static_cast<unsigned char>(buf[1]) <= 0xBF));
     956          61 :             buf[2] = '0';
     957          61 :             buf[3] = '1';
     958          61 :             buf[4] = '2';
     959          61 :             buf[5] = '3';
     960          61 :             buf[6] = '4';
     961          61 :             buf[7] = '\0';
     962             : 
     963          61 :             as2js::String str1;
     964          61 :             CPPUNIT_ASSERT(str1.from_utf8(buf) == as2js::String::conversion_result_t::STRING_BAD);
     965          61 :         }
     966             :     }
     967             :     // and now 10 random invalid strings
     968          10 :     for(int i(1); i < 10; ++i)
     969             :     {
     970             :         // verify size
     971             :         char buf[256];
     972        2304 :         for(int j(0); j < 255; ++j)
     973             :         {
     974        2302 :             do
     975             :             {
     976        2302 :                 buf[j] = rand();
     977             :             }
     978        2302 :             while(buf[j] == '\0');
     979             :         }
     980           9 :         buf[255] = '\0';
     981             : 
     982           9 :         char const *sub(buf);
     983           9 :         size_t length(255);
     984             :         uint32_t wc;
     985           9 :         as2js::String out;
     986           9 :         int r(0);
     987           9 :         as2js::String::conversion_result_t result(as2js::String::conversion_result_t::STRING_BAD);
     988          22 :         do
     989             :         {
     990          22 :             r = mbstowc(wc, sub, length);
     991          22 :             if(r > 0 && !as2js::String::valid_character(wc))
     992             :             {
     993           0 :                 result = as2js::String::conversion_result_t::STRING_INVALID;
     994           0 :                 r = -2;
     995           0 :                 break;
     996             :             }
     997             :         }
     998             :         while(r > 0);
     999           9 :         if(r != -2 && r != -3)
    1000             :         {
    1001             :             // a valid string?!
    1002           0 :             continue;
    1003             :         }
    1004             :         // all characters are good, but we may read the end early
    1005          18 :         as2js::String str3;
    1006           9 :         CPPUNIT_ASSERT(str3.from_utf8(buf) == result);
    1007           9 :         CPPUNIT_ASSERT(out.length() == str3.length());
    1008           9 :     }
    1009             : 
    1010             :     // characters over 0x10FFFF are all invalid
    1011           1 :     int counter(0);
    1012      261596 :     for(uint32_t i(0x110000); i < 0x80000000; i += rand() & 0x3FFF + 1, ++counter)
    1013             :     {
    1014      261595 :         CPPUNIT_ASSERT(!as2js::String::valid_character(i));
    1015             : 
    1016      261595 :         if(!as2js_test::g_gui && (counter & 0x00001FFF) == 0)
    1017             :         {
    1018          32 :             std::cout << "." << std::flush;
    1019             :         }
    1020             : 
    1021             :         // WARNING: cannot use the String to convert to wcs because
    1022             :         //          that catches those invalid characters too!
    1023             :         char buf[8];
    1024      261595 :         wctombs(buf, i);
    1025             : 
    1026      261595 :         as2js::String str1;
    1027      261595 :         CPPUNIT_ASSERT(str1.from_utf8(buf) == as2js::String::conversion_result_t::STRING_INVALID);
    1028      261595 :         CPPUNIT_ASSERT(str1.empty()); // not modified
    1029             : 
    1030      523190 :         as2js::String str2("old value");
    1031      261595 :         CPPUNIT_ASSERT(str2.from_utf8(buf) == as2js::String::conversion_result_t::STRING_INVALID);
    1032      261595 :         CPPUNIT_ASSERT(str2.length() == 9); // not modified
    1033      261595 :         CPPUNIT_ASSERT(str2 == "old value"); // not modified
    1034      261595 :         CPPUNIT_ASSERT(!(str2 != "old value")); // not modified
    1035      261595 :     }
    1036             : 
    1037             :     // any value that represents a negative number (int32_t) is
    1038             :     // so not valid that we cannot even encode it to test...
    1039             :     //for(uint32_t i(0x80000000); i < 0xFFFFFFFF; ++i)
    1040             :     //{
    1041             :     //    // WARNING: cannot use the String to convert to wcs because
    1042             :     //    //          that catches those invalid characters too!
    1043             :     //    char buf[8];
    1044             :     //    wctombs(buf, i);
    1045             : 
    1046             :     //    as2js::String str1;
    1047             :     //    CPPUNIT_ASSERT(str1.from_utf8(buf) == as2js::String::conversion_result_t::STRING_INVALID);
    1048             :     //    CPPUNIT_ASSERT(str1.empty()); // not modified
    1049             : 
    1050             :     //    as2js::String str2("old value");
    1051             :     //    CPPUNIT_ASSERT(str2.from_utf8(buf) == as2js::String::conversion_result_t::STRING_INVALID);
    1052             :     //    CPPUNIT_ASSERT(str2.length() == 9); // not modified
    1053             :     //    CPPUNIT_ASSERT(str2 == "old value"); // not modified
    1054             :     //}
    1055           1 : }
    1056             : 
    1057             : 
    1058           1 : void As2JsStringUnitTests::test_utf16()
    1059             : {
    1060             :     {
    1061           1 :         wchar_t const *null_wchar_ptr(nullptr);
    1062           1 :         as2js::String str1(null_wchar_ptr, 4);
    1063           1 :         CPPUNIT_ASSERT(&(str1 = "") == &str1);
    1064           1 :         CPPUNIT_ASSERT(str1.empty());
    1065           1 :         CPPUNIT_ASSERT(str1.length() == 0);
    1066           1 :         CPPUNIT_ASSERT(str1.utf8_length() == 0);
    1067           1 :         CPPUNIT_ASSERT("" == str1);
    1068           1 :         CPPUNIT_ASSERT(str1 == "");
    1069           1 :         CPPUNIT_ASSERT(!("" != str1));
    1070           1 :         CPPUNIT_ASSERT(!(str1 != ""));
    1071           1 :         CPPUNIT_ASSERT(str1.valid());
    1072             : 
    1073           2 :         as2js::String str2;
    1074           1 :         str2.from_wchar(null_wchar_ptr, 6);
    1075           1 :         CPPUNIT_ASSERT(&(str2 = "") == &str2);
    1076           1 :         CPPUNIT_ASSERT(str2.empty());
    1077           1 :         CPPUNIT_ASSERT(str2.length() == 0);
    1078           1 :         CPPUNIT_ASSERT(str2.utf8_length() == 0);
    1079           1 :         CPPUNIT_ASSERT("" == str2);
    1080           1 :         CPPUNIT_ASSERT(str2 == "");
    1081           1 :         CPPUNIT_ASSERT(!("" != str2));
    1082           1 :         CPPUNIT_ASSERT(!(str2 != ""));
    1083           2 :         CPPUNIT_ASSERT(str2.valid());
    1084             :     }
    1085             : 
    1086             :     // check all the characters (Except '\0' and surrogates)
    1087     1112065 :     for(int i(1); i < 0x110000; ++i)
    1088             :     {
    1089             :         // skip the surrogate which we want to encode from other characters
    1090             :         // rather than use as is...
    1091     1112064 :         if(i == 0x00D800)
    1092             :         {
    1093           1 :             i = 0x00DFFF;
    1094           1 :             continue;
    1095             :         }
    1096             : 
    1097     1112063 :         CPPUNIT_ASSERT(as2js::String::valid_character(i));
    1098             : 
    1099     1112063 :         if(!as2js_test::g_gui && (i & 0x001FFF) == 0)
    1100             :         {
    1101         135 :             std::cout << "." << std::flush;
    1102             :         }
    1103             : 
    1104             :         // Note: although wchar_t is 32 bits under Linux, we manage these
    1105             :         //       strings as if they were 16 bits... (although we'll
    1106             :         //       accept characters larger than 0x00FFFF as a UTF-32
    1107             :         //       character.)
    1108             :         wchar_t buf[10];
    1109     1112063 :         if(i >= 0x10000)
    1110             :         {
    1111     1048576 :             buf[0] = ((i - 0x10000) >> 10) | 0xD800;    // lead
    1112     1048576 :             buf[1] = ((i - 0x10000) & 0x3FF) | 0xDC00;  // trail
    1113     1048576 :             buf[2] = '\0';
    1114             :         }
    1115             :         else
    1116             :         {
    1117       63487 :             buf[0] = i;
    1118       63487 :             buf[1] = '\0';
    1119             :         }
    1120             : 
    1121             :         {
    1122     1112063 :             as2js::String str1;
    1123     1112063 :             CPPUNIT_ASSERT(str1.from_wchar(buf) == as2js::String::conversion_result_t::STRING_GOOD);
    1124     1112063 :             CPPUNIT_ASSERT(1 == str1.length());
    1125             :             //CPPUNIT_ASSERT(buf == str1); -- we do not support those
    1126             :             //CPPUNIT_ASSERT(str1 == buf);
    1127     1112063 :             CPPUNIT_ASSERT(str1.valid());
    1128             : 
    1129             :             // try copies of strings created from wchar_t characters
    1130     2224126 :             as2js::String str2(str1);
    1131     1112063 :             CPPUNIT_ASSERT(1 == str2.length());
    1132     1112063 :             CPPUNIT_ASSERT(str1 == str2);
    1133     1112063 :             CPPUNIT_ASSERT(!(str1 != str2));
    1134     1112063 :             CPPUNIT_ASSERT(str2.valid());
    1135             : 
    1136             :             // now test the += of a wchar_t
    1137             :             // TODO under MS-Windows we cannot test this += with
    1138             :             //      characters larger than 0x0FFFF
    1139     1112063 :             CPPUNIT_ASSERT(&(str1 += static_cast<wchar_t>(i)) == &str1);
    1140     1112063 :             CPPUNIT_ASSERT(2 == str1.length());
    1141     1112063 :             CPPUNIT_ASSERT(str1.valid());
    1142             : 
    1143     1112063 :             CPPUNIT_ASSERT(&(str1 += utf16_to_append) == &str1);
    1144     1112063 :             CPPUNIT_ASSERT(5 == str1.length());
    1145     1112063 :             CPPUNIT_ASSERT(str1.valid());
    1146     1112063 :             CPPUNIT_ASSERT(str1[2] == 0x1111);
    1147     1112063 :             CPPUNIT_ASSERT(str1[3] == 0x2222);
    1148     1112063 :             CPPUNIT_ASSERT(str1[4] == 0x3333);
    1149             : 
    1150             :             // try copies of strings created from wchar_t characters
    1151     2224126 :             as2js::String str6;
    1152     1112063 :             CPPUNIT_ASSERT(&(str6 = str2) == &str6);
    1153     1112063 :             CPPUNIT_ASSERT(1 == str6.length());
    1154     1112063 :             CPPUNIT_ASSERT(str2 == str6);
    1155     1112063 :             CPPUNIT_ASSERT(!(str2 != str6));
    1156     2224126 :             CPPUNIT_ASSERT(str6.valid());
    1157             :         }
    1158             : 
    1159             :         // just in case, try without the surrogate if wchar_t is > 2
    1160     1112063 :         if(sizeof(wchar_t) > 2 && i > 0xFFFF)
    1161             :         {
    1162     1048576 :             buf[0] = i;
    1163     1048576 :             buf[1] = '\0';
    1164             : 
    1165             :             {
    1166     1048576 :                 as2js::String str3;
    1167     1048576 :                 CPPUNIT_ASSERT(str3.from_wchar(buf) == as2js::String::conversion_result_t::STRING_GOOD);
    1168     1048576 :                 CPPUNIT_ASSERT(1 == str3.length());
    1169     1048576 :                 CPPUNIT_ASSERT(str3.valid());
    1170             : 
    1171             :                 // try copies of strings created from wchar_t characters
    1172     2097152 :                 as2js::String str4(str3);
    1173     1048576 :                 CPPUNIT_ASSERT(1 == str4.length());
    1174     1048576 :                 CPPUNIT_ASSERT(str3 == str4);
    1175     1048576 :                 CPPUNIT_ASSERT(!(str3 != str4));
    1176     1048576 :                 CPPUNIT_ASSERT(str4.valid());
    1177             : 
    1178     2097152 :                 std::wstring wstr(utf16_to_append);
    1179     1048576 :                 CPPUNIT_ASSERT(&(str3 += wstr) == &str3);
    1180     1048576 :                 CPPUNIT_ASSERT(4 == str3.length());
    1181     1048576 :                 CPPUNIT_ASSERT(str3.valid());
    1182     1048576 :                 CPPUNIT_ASSERT(str3[1] == 0x1111);
    1183     1048576 :                 CPPUNIT_ASSERT(str3[2] == 0x2222);
    1184     1048576 :                 CPPUNIT_ASSERT(str3[3] == 0x3333);
    1185             : 
    1186     1048576 :                 CPPUNIT_ASSERT(&(str3 = wstr) == &str3);
    1187     1048576 :                 CPPUNIT_ASSERT(3 == str3.length());
    1188     1048576 :                 CPPUNIT_ASSERT(str3.valid());
    1189     1048576 :                 CPPUNIT_ASSERT(str3[0] == 0x1111);
    1190     1048576 :                 CPPUNIT_ASSERT(str3[1] == 0x2222);
    1191     2097152 :                 CPPUNIT_ASSERT(str3[2] == 0x3333);
    1192             :             }
    1193             :         }
    1194             : 
    1195             :         // try with a string of a respectful size (really small though)
    1196             :         // and the operator = (wchar_t const *) function
    1197             :         {
    1198             :             // repeat 5 times
    1199     6672378 :             for(int j(0); j < 5; ++j)
    1200             :             {
    1201    44799955 :                 for(int k(0); k < 8; ++k)
    1202             :                 {
    1203    39239640 :                     if(k == 4)
    1204             :                     {
    1205     5560315 :                         if(i >= 0x10000)
    1206             :                         {
    1207     5242880 :                             buf[k] = ((i - 0x10000) >> 10) | 0xD800;    // lead
    1208     5242880 :                             ++k;
    1209     5242880 :                             buf[k] = ((i - 0x10000) & 0x3FF) | 0xDC00;  // trail
    1210             :                         }
    1211             :                         else
    1212             :                         {
    1213      317435 :                             buf[k] = i;
    1214             :                         }
    1215             :                     }
    1216             :                     else
    1217             :                     {
    1218             :                         // if not offset 4, get a random character
    1219             :                         // in BMP 0 which are not '\0' nor a surrogate
    1220    34764170 :                         do
    1221             :                         {
    1222    34764170 :                             buf[k] = rand() & 0x00FFFF;
    1223             :                         }
    1224    69527797 :                         while(buf[k] == '\0' || (buf[k] >= 0xD800 && buf[k] <= 0xDFFF));
    1225             :                     }
    1226             :                 }
    1227     5560315 :                 buf[8] = '\0';
    1228             : 
    1229             :                 // we verify the constructor, so we know it works...
    1230     5560315 :                 as2js::String str_cmp(buf);
    1231    11120630 :                 as2js::String str9("original");
    1232     5560315 :                 CPPUNIT_ASSERT(&(str9 = buf) == &str9);
    1233     5560315 :                 CPPUNIT_ASSERT((i >= 0x10000 ? 7 : 8) == str9.length());
    1234     5560315 :                 CPPUNIT_ASSERT(str9 == str9);
    1235     5560315 :                 CPPUNIT_ASSERT(str9 == str_cmp);
    1236     5560315 :                 CPPUNIT_ASSERT(str_cmp == str_cmp);
    1237     5560315 :                 CPPUNIT_ASSERT(!(str9 != str9));
    1238     5560315 :                 CPPUNIT_ASSERT(!(str9 != str_cmp));
    1239     5560315 :                 CPPUNIT_ASSERT(!(str_cmp != str_cmp));
    1240     5560315 :                 CPPUNIT_ASSERT(str9.valid());
    1241             : 
    1242    11120630 :                 std::wstring wstd(buf);
    1243    11120630 :                 as2js::String str10("original");
    1244     5560315 :                 CPPUNIT_ASSERT(&(str10 = buf) == &str10);
    1245     5560315 :                 CPPUNIT_ASSERT((i >= 0x10000 ? 7 : 8) == str10.length());
    1246     5560315 :                 CPPUNIT_ASSERT(str10 == str10);
    1247     5560315 :                 CPPUNIT_ASSERT(str10 == str_cmp);
    1248     5560315 :                 CPPUNIT_ASSERT(str_cmp == str_cmp);
    1249     5560315 :                 CPPUNIT_ASSERT(!(str10 != str10));
    1250     5560315 :                 CPPUNIT_ASSERT(!(str10 != str_cmp));
    1251     5560315 :                 CPPUNIT_ASSERT(!(str_cmp != str_cmp));
    1252     5560315 :                 CPPUNIT_ASSERT(str10.valid());
    1253     5560315 :             }
    1254             :         }
    1255             : 
    1256             :         // test that we detect lead without trail surrogates
    1257     1112063 :         if(i >= 0x10000)
    1258             :         {
    1259             :             // inverted, oops!
    1260     6291456 :             for(int j(0); j < 5; ++j)
    1261             :             {
    1262     5411754 :                 do
    1263             :                 {
    1264             :                     // generate a random character in the first spot
    1265     5411754 :                     buf[j] = rand() & 0x00FFFF;
    1266             :                 }
    1267    10823416 :                 while(buf[j] == '\0' || (buf[j] >= 0xD800 && buf[j] <= 0xDFFF));
    1268             :             }
    1269     1048576 :             buf[5] = ((i - 0x10000) >> 10) | 0xD800;    // lead
    1270     1048576 :             buf[6] = '\0';
    1271             : 
    1272     1048576 :             as2js::String str7("original");
    1273     1048576 :             CPPUNIT_ASSERT(str7.from_wchar(buf) == as2js::String::conversion_result_t::STRING_END);
    1274     1048576 :             CPPUNIT_ASSERT(8 == str7.length());
    1275     1048576 :             CPPUNIT_ASSERT("original" == str7);
    1276     1048576 :             CPPUNIT_ASSERT(!("original" != str7));
    1277     1048576 :             CPPUNIT_ASSERT(str7.valid());
    1278             : 
    1279     2097152 :             as2js::String str8("original");
    1280     1048576 :             CPPUNIT_ASSERT(str8.from_wchar(buf, 6) == as2js::String::conversion_result_t::STRING_END);
    1281     1048576 :             CPPUNIT_ASSERT(8 == str8.length());
    1282     1048576 :             CPPUNIT_ASSERT("original" == str8);
    1283     1048576 :             CPPUNIT_ASSERT(!("original" != str8));
    1284     2097152 :             CPPUNIT_ASSERT(str8.valid());
    1285             :         }
    1286             : 
    1287             :         // test that we detect inverted surrogates
    1288     1112063 :         if(i >= 0x10000)
    1289             :         {
    1290             :             // inverted, oops!
    1291     1048576 :             buf[0] = ((i - 0x10000) & 0x3FF) | 0xDC00;  // trail
    1292     1048576 :             buf[1] = ((i - 0x10000) >> 10) | 0xD800;    // lead
    1293     1048576 :             buf[2] = '\0';
    1294             : 
    1295     1048576 :             as2js::String str7("original");
    1296     1048576 :             CPPUNIT_ASSERT(str7.from_wchar(buf) == as2js::String::conversion_result_t::STRING_BAD);
    1297     1048576 :             CPPUNIT_ASSERT(8 == str7.length());
    1298     1048576 :             CPPUNIT_ASSERT("original" == str7);
    1299     1048576 :             CPPUNIT_ASSERT(!("original" != str7));
    1300     1048576 :             CPPUNIT_ASSERT(str7.valid());
    1301             : 
    1302     1048576 :             buf[2] = rand();
    1303     1048576 :             buf[3] = rand();
    1304     1048576 :             buf[4] = rand();
    1305     1048576 :             buf[5] = rand();
    1306     1048576 :             buf[6] = rand();
    1307     1048576 :             buf[7] = rand();
    1308     1048576 :             buf[8] = rand();
    1309     1048576 :             buf[9] = rand();
    1310     2097152 :             as2js::String str11("original");
    1311     1048576 :             CPPUNIT_ASSERT(str11.from_wchar(buf, rand() % 8 + 2) == as2js::String::conversion_result_t::STRING_BAD);
    1312     1048576 :             CPPUNIT_ASSERT(8 == str11.length());
    1313     1048576 :             CPPUNIT_ASSERT("original" == str11);
    1314     1048576 :             CPPUNIT_ASSERT(!("original" != str11));
    1315     2097152 :             CPPUNIT_ASSERT(str11.valid());
    1316             :         }
    1317             :     }
    1318           1 : }
    1319             : 
    1320             : 
    1321           1 : void As2JsStringUnitTests::test_utf32()
    1322             : {
    1323             :     {
    1324           1 :         as2js::as_char_t const *null_char32_ptr(nullptr);
    1325           1 :         as2js::String str1(null_char32_ptr, 9);
    1326           1 :         CPPUNIT_ASSERT(&(str1 = "") == &str1);
    1327           1 :         CPPUNIT_ASSERT(str1.empty());
    1328           1 :         CPPUNIT_ASSERT(str1.length() == 0);
    1329           1 :         CPPUNIT_ASSERT(str1.utf8_length() == 0);
    1330           1 :         CPPUNIT_ASSERT("" == str1);
    1331           1 :         CPPUNIT_ASSERT(str1 == "");
    1332           1 :         CPPUNIT_ASSERT(!("" != str1));
    1333           1 :         CPPUNIT_ASSERT(!(str1 != ""));
    1334           1 :         CPPUNIT_ASSERT(str1.valid());
    1335             : 
    1336           2 :         as2js::String str2;
    1337           1 :         str2.from_as_char(null_char32_ptr, 6);
    1338           1 :         CPPUNIT_ASSERT(&(str2 = "") == &str2);
    1339           1 :         CPPUNIT_ASSERT(str2.empty());
    1340           1 :         CPPUNIT_ASSERT(str2.length() == 0);
    1341           1 :         CPPUNIT_ASSERT(str2.utf8_length() == 0);
    1342           1 :         CPPUNIT_ASSERT("" == str2);
    1343           1 :         CPPUNIT_ASSERT(str2 == "");
    1344           1 :         CPPUNIT_ASSERT(!("" != str2));
    1345           1 :         CPPUNIT_ASSERT(!(str2 != ""));
    1346           2 :         CPPUNIT_ASSERT(str2.valid());
    1347             :     }
    1348             : 
    1349             :     // check all the characters (Except '\0' and surrogates)
    1350     1112065 :     for(int i(1); i < 0x110000; ++i)
    1351             :     {
    1352             :         // skip the surrogate which we want to encode from other characters
    1353             :         // rather than use as is...
    1354     1112064 :         if(i == 0x00D800)
    1355             :         {
    1356           1 :             i = 0x00DFFF;
    1357           1 :             continue;
    1358             :         }
    1359             : 
    1360     1112063 :         if(!as2js_test::g_gui && (i & 0x00FFFF) == 0)
    1361             :         {
    1362          16 :             std::cout << "." << std::flush;
    1363             :         }
    1364             : 
    1365             :         // Note: although wchar_t is 32 bits under Linux, we manage these
    1366             :         //       strings as if they were 16 bits... (although we'll
    1367             :         //       accept characters larger than 0x00FFFF as a UTF-32
    1368             :         //       character.)
    1369             :         as2js::as_char_t buf[2];
    1370     1112063 :         buf[0] = i;
    1371     1112063 :         buf[1] = '\0';
    1372             : 
    1373             :         {
    1374     1112063 :             as2js::String str1;
    1375     1112063 :             CPPUNIT_ASSERT(str1.from_as_char(buf) == as2js::String::conversion_result_t::STRING_GOOD);
    1376     1112063 :             CPPUNIT_ASSERT(1 == str1.length());
    1377     1112063 :             CPPUNIT_ASSERT(buf == str1);
    1378     1112063 :             CPPUNIT_ASSERT(str1 == buf);
    1379     1112063 :             CPPUNIT_ASSERT(!(str1 != buf));
    1380     1112063 :             CPPUNIT_ASSERT(str1.valid());
    1381             : 
    1382             :             // try copies of strings created from wchar_t characters
    1383     2224126 :             as2js::String str2(str1);
    1384     1112063 :             CPPUNIT_ASSERT(1 == str2.length());
    1385     1112063 :             CPPUNIT_ASSERT(str1 == str2);
    1386     1112063 :             CPPUNIT_ASSERT(!(str1 != str2));
    1387     1112063 :             CPPUNIT_ASSERT(str2.valid());
    1388             : 
    1389             :             // now test the += of a wchar_t
    1390             :             // TODO under MS-Windows we cannot test this += with
    1391             :             //      characters larger than 0x0FFFF
    1392     1112063 :             CPPUNIT_ASSERT(&(str1 += static_cast<as2js::as_char_t>(i)) == &str1);
    1393     1112063 :             CPPUNIT_ASSERT(2 == str1.length());
    1394     1112063 :             CPPUNIT_ASSERT(str1.valid());
    1395     1112063 :             CPPUNIT_ASSERT(str1[0] == i);
    1396     1112063 :             CPPUNIT_ASSERT(str1[1] == i);
    1397             : 
    1398     1112063 :             CPPUNIT_ASSERT(&(str1 += utf32_to_append) == &str1);
    1399     1112063 :             CPPUNIT_ASSERT(5 == str1.length());
    1400     1112063 :             CPPUNIT_ASSERT(str1.valid());
    1401     1112063 :             CPPUNIT_ASSERT(str1[0] == i);
    1402     1112063 :             CPPUNIT_ASSERT(str1[1] == i);
    1403     1112063 :             CPPUNIT_ASSERT(str1[2] == 0x101111);
    1404     1112063 :             CPPUNIT_ASSERT(str1[3] == 0x5555);
    1405     2224126 :             CPPUNIT_ASSERT(str1[4] == 0x103333);
    1406             :         }
    1407             :     }
    1408             : 
    1409             :     // some random strings to test the length on the constructor
    1410          51 :     for(int i(0); i < 50; ++i)
    1411             :     {
    1412             :         as2js::as_char_t buf[256];
    1413       12800 :         for(int j(0); j < 255; ++j)
    1414             :         {
    1415       23893 :             do
    1416             :             {
    1417       23893 :                 buf[j] = rand() & 0x001FFFFF;
    1418             :             }
    1419       47786 :             while(buf[j] == '\0' || buf[j] > 0x0010FFFF || (buf[j] >= 0xD800 && buf[j] <= 0xDFFF));
    1420             :         }
    1421          50 :         buf[255] = '\0';
    1422             : 
    1423             :         // the whole string first
    1424          50 :         as2js::String str1(buf);
    1425          50 :         CPPUNIT_ASSERT(255 == str1.length());
    1426          50 :         CPPUNIT_ASSERT(buf == str1);
    1427          50 :         CPPUNIT_ASSERT(str1 == buf);
    1428          50 :         CPPUNIT_ASSERT(!(buf != str1));
    1429          50 :         CPPUNIT_ASSERT(!(str1 != buf));
    1430          50 :         CPPUNIT_ASSERT(str1.valid());
    1431             : 
    1432             :         // try again with the from_as_char()
    1433          50 :         CPPUNIT_ASSERT(str1.from_as_char(buf) == as2js::String::conversion_result_t::STRING_GOOD);
    1434          50 :         CPPUNIT_ASSERT(255 == str1.length());
    1435          50 :         CPPUNIT_ASSERT(buf == str1);
    1436          50 :         CPPUNIT_ASSERT(str1 == buf);
    1437          50 :         CPPUNIT_ASSERT(!(buf != str1));
    1438          50 :         CPPUNIT_ASSERT(!(str1 != buf));
    1439          50 :         CPPUNIT_ASSERT(str1.valid());
    1440             : 
    1441             :         // now test different sizes
    1442        2550 :         for(int j(0); j < 50; ++j)
    1443             :         {
    1444        2500 :             size_t size(rand() % 250 + 2);
    1445             : 
    1446             :             // the whole string first
    1447        2500 :             as2js::String str2(buf, size);
    1448        2500 :             CPPUNIT_ASSERT(size == str2.length());
    1449        2500 :             as2js::as_char_t save_a(buf[size]);
    1450        2500 :             buf[size] = '\0';
    1451        2500 :             CPPUNIT_ASSERT(buf == str2);
    1452        2500 :             CPPUNIT_ASSERT(str2 == buf);
    1453        2500 :             CPPUNIT_ASSERT(!(buf != str2));
    1454        2500 :             CPPUNIT_ASSERT(!(str2 != buf));
    1455        2500 :             CPPUNIT_ASSERT(str2.valid());
    1456        2500 :             buf[size] = save_a;
    1457             : 
    1458             :             // try again with the from_as_char()
    1459        2500 :             CPPUNIT_ASSERT(str2.from_as_char(buf, size) == as2js::String::conversion_result_t::STRING_GOOD);
    1460        2500 :             CPPUNIT_ASSERT(size == str2.length());
    1461        2500 :             as2js::as_char_t save_b(buf[size]);
    1462        2500 :             buf[size] = '\0';
    1463        2500 :             CPPUNIT_ASSERT(buf == str2);
    1464        2500 :             CPPUNIT_ASSERT(str2 == buf);
    1465        2500 :             CPPUNIT_ASSERT(!(buf != str2));
    1466        2500 :             CPPUNIT_ASSERT(!(str2 != buf));
    1467        2500 :             CPPUNIT_ASSERT(str2.valid());
    1468        2500 :             buf[size] = save_b;
    1469             : 
    1470             :             // this should not have changed
    1471        2500 :             CPPUNIT_ASSERT(255 == str1.length());
    1472             : 
    1473             :             // take a minute to test str1 += str2
    1474             :             {
    1475             :                 // make a copy otherwise str1 += str2 becomes cumulative
    1476        2500 :                 as2js::String str3(str1);
    1477             : 
    1478             :                 as2js::as_char_t buf2[512];
    1479        2500 :                 memcpy(buf2, buf, sizeof(buf[0]) * 255);
    1480        2500 :                 memcpy(buf2 + 255, buf, sizeof(buf[0]) * size);  // then what was copied in str2
    1481        2500 :                 buf2[255 + size] = '\0';
    1482        2500 :                 str3 += str2;
    1483        2500 :                 CPPUNIT_ASSERT(size + 255 == str3.length());
    1484        2500 :                 CPPUNIT_ASSERT(buf2 == str3);
    1485        2500 :                 CPPUNIT_ASSERT(str3 == buf2);
    1486        2500 :                 CPPUNIT_ASSERT(!(buf2 != str3));
    1487        2500 :                 CPPUNIT_ASSERT(!(str3 != buf2));
    1488        2500 :                 CPPUNIT_ASSERT(str3.valid());
    1489             : 
    1490             :                 // and make sure that str2 was indeed untouched
    1491        2500 :                 CPPUNIT_ASSERT(size == str2.length());
    1492        2500 :                 as2js::as_char_t save_c(buf[size]);
    1493        2500 :                 buf[size] = '\0';
    1494        2500 :                 CPPUNIT_ASSERT(buf == str2);
    1495        2500 :                 CPPUNIT_ASSERT(str2 == buf);
    1496        2500 :                 CPPUNIT_ASSERT(!(buf != str2));
    1497        2500 :                 CPPUNIT_ASSERT(!(str2 != buf));
    1498        2500 :                 CPPUNIT_ASSERT(str2.valid());
    1499        2500 :                 buf[size] = save_c;
    1500             :             }
    1501             : 
    1502             :             // try again with the from_as_char()
    1503        2500 :             int const bad_pos(size / 2);
    1504        2500 :             as2js::as_char_t save_d(buf[bad_pos]);
    1505        2501 :             do
    1506             :             {
    1507        2501 :                 buf[bad_pos] = rand();
    1508             :             }
    1509        5002 :             while((buf[bad_pos] > 0 && buf[bad_pos] < 0xD800)
    1510        2501 :                || (buf[bad_pos] > 0xDFFF && buf[bad_pos] < 0x110000));
    1511        5000 :             as2js::String str4;
    1512        2500 :             CPPUNIT_ASSERT(str4.from_as_char(buf, size) == as2js::String::conversion_result_t::STRING_INVALID);
    1513        2500 :             CPPUNIT_ASSERT(0 == str4.length());
    1514        2500 :             CPPUNIT_ASSERT(str4.empty());
    1515        2500 :             CPPUNIT_ASSERT("" == str4);
    1516        2500 :             CPPUNIT_ASSERT(str4 == "");
    1517        2500 :             CPPUNIT_ASSERT(!("" != str4));
    1518        2500 :             CPPUNIT_ASSERT(!(str4 != ""));
    1519        2500 :             CPPUNIT_ASSERT(str4.valid());
    1520        2500 :             buf[bad_pos] = save_d;
    1521             : 
    1522             :             // test a copy of str1 with one invalid character
    1523        5000 :             as2js::String str5(str1);
    1524        2500 :             do
    1525             :             {
    1526             :                 // testing that indeed the [] operator does not check the
    1527             :                 // validity of UTF-32 characters...
    1528             :                 // std::basic_string<as_char_t> operator []()
    1529        2500 :                 str5[bad_pos] = rand();
    1530             :             }
    1531        5000 :             while((str5[bad_pos] > 0 && str5[bad_pos] < 0xD800)
    1532        5000 :                || (str5[bad_pos] > 0xDFFF && str5[bad_pos] < 0x110000));
    1533        2500 :             CPPUNIT_ASSERT(!str5.valid());
    1534             :             // if invalid the UTF-8 length is always -1
    1535        2500 :             CPPUNIT_ASSERT(str5.utf8_length() == -1);
    1536        2500 :         }
    1537             : 
    1538             :         //
    1539          50 :     }
    1540             : 
    1541             :     // test that the surrogate all crap out
    1542        2049 :     for(int i(0xD800); i < 0xE000; ++i)
    1543             :     {
    1544             :         as2js::as_char_t buf[2];
    1545        2048 :         buf[0] = i;
    1546        2048 :         buf[1] = '\0';
    1547             : 
    1548        2048 :         as2js::String str1;
    1549        2048 :         CPPUNIT_ASSERT(str1.from_as_char(buf) == as2js::String::conversion_result_t::STRING_INVALID);
    1550        2048 :         CPPUNIT_ASSERT(str1.empty()); // not modified
    1551             : 
    1552        4096 :         as2js::String str2("old value");
    1553        2048 :         CPPUNIT_ASSERT(str2.from_as_char(buf) == as2js::String::conversion_result_t::STRING_INVALID);
    1554        2048 :         CPPUNIT_ASSERT(str2.length() == 9); // not modified
    1555        2048 :         CPPUNIT_ASSERT(str2 == "old value"); // not modified
    1556        2048 :         CPPUNIT_ASSERT(!(str2 != "old value")); // not modified
    1557        2048 :         CPPUNIT_ASSERT(str2 != "new value");
    1558             : 
    1559        4096 :         as2js::String str3;
    1560        2048 :         CPPUNIT_ASSERT(str3.from_as_char(buf, 1) == as2js::String::conversion_result_t::STRING_INVALID);
    1561        2048 :         CPPUNIT_ASSERT(str3.empty()); // not modified
    1562             : 
    1563        4096 :         as2js::String str4("old value");
    1564        2048 :         CPPUNIT_ASSERT(str4.from_as_char(buf, 1) == as2js::String::conversion_result_t::STRING_INVALID);
    1565        2048 :         CPPUNIT_ASSERT(str4.length() == 9); // not modified
    1566        2048 :         CPPUNIT_ASSERT(str4 == "old value"); // not modified
    1567        2048 :         CPPUNIT_ASSERT(!(str4 != "old value")); // not modified
    1568        2048 :         CPPUNIT_ASSERT(str4 != "new value");
    1569        2048 :     }
    1570             : 
    1571             :     // characters over 0x10FFFF are all invalid
    1572             :     //
    1573             :     // NOTE: In this case the loop index (i) will wrap around and we
    1574             :     //       catch that using that wierd test you see below
    1575             :     //
    1576           1 :     int counter(0);
    1577      524000 :     for(uint32_t i(0x110000); i >= 0x00110000; i += rand() & 0x3FFF + 1, ++counter)
    1578             :     {
    1579             :         // test this one because it may not have been tested yet
    1580      523999 :         CPPUNIT_ASSERT(!as2js::String::valid_character(i));
    1581             : 
    1582      523999 :         if(!as2js_test::g_gui && (counter & 0x00001FFF) == 0)
    1583             :         {
    1584          64 :             std::cout << "." << std::flush;
    1585             :         }
    1586             : 
    1587             :         // WARNING: cannot use the String to convert to wcs because
    1588             :         //          that catches those invalid characters too!
    1589             :         as2js::as_char_t buf[8];
    1590      523999 :         buf[0] = i;
    1591      523999 :         buf[1] = '\0';
    1592             : 
    1593      523999 :         as2js::String str1;
    1594      523999 :         CPPUNIT_ASSERT(str1.from_as_char(buf) == as2js::String::conversion_result_t::STRING_INVALID);
    1595      523999 :         CPPUNIT_ASSERT(str1.empty()); // not modified
    1596             : 
    1597     1047998 :         as2js::String str2("old value");
    1598      523999 :         CPPUNIT_ASSERT(str2.from_as_char(buf) == as2js::String::conversion_result_t::STRING_INVALID);
    1599      523999 :         CPPUNIT_ASSERT(str2.length() == 9); // not modified
    1600      523999 :         CPPUNIT_ASSERT(str2 == "old value"); // not modified
    1601      523999 :         CPPUNIT_ASSERT(!(str2 != "old value")); // not modified
    1602      523999 :         CPPUNIT_ASSERT(str2 != "new value");
    1603      523999 :     }
    1604           1 : }
    1605             : 
    1606             : 
    1607           1 : void As2JsStringUnitTests::test_number()
    1608             : {
    1609             :     {
    1610             :         // empty is a special case that represents 0 or 0.0
    1611           1 :         as2js::String str1;
    1612             : 
    1613           1 :         CPPUNIT_ASSERT(str1.is_int64());
    1614           1 :         CPPUNIT_ASSERT(str1.is_float64());
    1615           1 :         CPPUNIT_ASSERT(str1.is_number());
    1616           1 :         CPPUNIT_ASSERT(str1.to_int64() == 0);
    1617             : #pragma GCC diagnostic push
    1618             : #pragma GCC diagnostic ignored "-Wfloat-equal"
    1619           1 :         CPPUNIT_ASSERT(str1.to_float64() == 0.0);
    1620             : #pragma GCC diagnostic pop
    1621           1 :         CPPUNIT_ASSERT(!str1.is_true());
    1622             : 
    1623             :         // "0x" or "0X" are not valid hexadecimal numbers
    1624           2 :         as2js::String str2;
    1625           1 :         CPPUNIT_ASSERT(&(str2 = "0x") == &str2);
    1626           1 :         CPPUNIT_ASSERT(!str2.is_int64());
    1627           1 :         CPPUNIT_ASSERT(!str2.is_float64());
    1628           1 :         CPPUNIT_ASSERT(!str2.is_number());
    1629           1 :         CPPUNIT_ASSERT_THROW(str2.to_int64(), as2js::exception_internal_error);
    1630           1 :         CPPUNIT_ASSERT(std::isnan(str2.to_float64()));
    1631           1 :         CPPUNIT_ASSERT(str2.is_true());
    1632             : 
    1633           2 :         as2js::String str3;
    1634           1 :         CPPUNIT_ASSERT(&(str3 = "0X") == &str3);
    1635           1 :         CPPUNIT_ASSERT(!str3.is_int64());
    1636           1 :         CPPUNIT_ASSERT(!str3.is_float64());
    1637           1 :         CPPUNIT_ASSERT(!str3.is_number());
    1638           1 :         CPPUNIT_ASSERT_THROW(str3.to_int64(), as2js::exception_internal_error);
    1639           1 :         CPPUNIT_ASSERT(std::isnan(str3.to_float64()));
    1640           2 :         CPPUNIT_ASSERT(str3.is_true());
    1641             :     }
    1642             : 
    1643      200002 :     for(int64_t i(-100000); i <= 100000; ++i)
    1644             :     {
    1645             :         // decimal
    1646             :         {
    1647      200001 :             std::stringstream str;
    1648      200001 :             str << (i >= 0 && (rand() & 1) ? "+" : "") << i;
    1649      400002 :             as2js::String str1(str.str());
    1650      200001 :             CPPUNIT_ASSERT(str1.is_int64());
    1651      200001 :             CPPUNIT_ASSERT(str1.is_float64());
    1652      200001 :             CPPUNIT_ASSERT(str1.is_number());
    1653      200001 :             CPPUNIT_ASSERT(str1.to_int64() == i);
    1654             : #pragma GCC diagnostic push
    1655             : #pragma GCC diagnostic ignored "-Wfloat-equal"
    1656      200001 :             CPPUNIT_ASSERT(str1.to_float64() == static_cast<double>(i));
    1657             : #pragma GCC diagnostic pop
    1658      400002 :             CPPUNIT_ASSERT(str1.is_true());
    1659             :         }
    1660             :         // hexadecimal
    1661             :         {
    1662      200001 :             std::stringstream str;
    1663      200001 :             str << (i < 0 ? "-" : (rand() & 1 ? "+" : "")) << "0" << (rand() & 1 ? "x" : "X") << std::hex << labs(i);
    1664      400002 :             as2js::String str1(str.str());
    1665      200001 :             CPPUNIT_ASSERT(str1.is_int64());
    1666      200001 :             CPPUNIT_ASSERT(!str1.is_float64());
    1667      200001 :             CPPUNIT_ASSERT(str1.is_number());
    1668      200001 :             CPPUNIT_ASSERT(str1.to_int64() == i);
    1669      200001 :             CPPUNIT_ASSERT(std::isnan(str1.to_float64()));
    1670      400002 :             CPPUNIT_ASSERT(str1.is_true());
    1671             :         }
    1672             :     }
    1673             : 
    1674        3398 :     for(double i(-1000.00); i <= 1000.00; i += (rand() % 120) / 100.0)
    1675             :     {
    1676        3397 :         std::stringstream str;
    1677        3397 :         str << i;
    1678       13588 :         if(str.str().find('e') != std::string::npos
    1679       13588 :         || str.str().find('E') != std::string::npos)
    1680             :         {
    1681             :             // this happens with numbers very close to zero and the
    1682             :             // system decides to write them as '1e-12' for example
    1683           0 :             continue;
    1684             :         }
    1685        6794 :         std::string value1(str.str());
    1686        6794 :         as2js::String str1(value1);
    1687        3397 :         int64_t integer1(lrint(i));
    1688        3397 :         bool is_integer1(std::find(value1.begin(), value1.end(), '.') == value1.end());
    1689        3397 :         CPPUNIT_ASSERT(str1.is_int64() ^ !is_integer1);
    1690        3397 :         CPPUNIT_ASSERT(str1.is_float64());
    1691        3397 :         CPPUNIT_ASSERT(str1.is_number());
    1692        3397 :         if(is_integer1)
    1693             :         {
    1694          38 :             CPPUNIT_ASSERT(str1.to_int64() == integer1);
    1695             :         }
    1696             :         else
    1697             :         {
    1698        3359 :             CPPUNIT_ASSERT_THROW(str1.to_int64(), as2js::exception_internal_error);
    1699             :         }
    1700        3397 :         CPPUNIT_ASSERT(close_double(str1.to_float64(), i, 0.01));
    1701             : 
    1702             : #pragma GCC diagnostic push
    1703             : #pragma GCC diagnostic ignored "-Wfloat-equal"
    1704        3397 :         CPPUNIT_ASSERT(str1.is_true());
    1705             : #pragma GCC diagnostic pop
    1706             : 
    1707             :         // add x 1000 as an exponent
    1708        3397 :         str << "e" << (rand() & 1 ? "+" : "") << "3";
    1709        6794 :         std::string value2(str.str());
    1710        6794 :         as2js::String str2(value2);
    1711             :         // the 'e' "breaks" the integer test in JavaScript
    1712        3397 :         CPPUNIT_ASSERT(!str2.is_int64());
    1713        3397 :         CPPUNIT_ASSERT(str2.is_float64());
    1714        3397 :         CPPUNIT_ASSERT(str2.is_number());
    1715        3397 :         CPPUNIT_ASSERT_THROW(str2.to_int64(), as2js::exception_internal_error);
    1716        3397 :         CPPUNIT_ASSERT(close_double(str2.to_float64(), i * 1000.0, 0.01));
    1717             : 
    1718             : #pragma GCC diagnostic push
    1719             : #pragma GCC diagnostic ignored "-Wfloat-equal"
    1720        3397 :         CPPUNIT_ASSERT(str2.is_true());
    1721             : #pragma GCC diagnostic pop
    1722             : 
    1723             :         // add x 1000 as an exponent
    1724        3397 :         str.str(""); // reset the string
    1725        3397 :         str << value1 << "e-3";
    1726        6794 :         std::string value3(str.str());
    1727        6794 :         as2js::String str3(value3);
    1728             :         // the 'e' "breaks" the integer test in JavaScript
    1729        3397 :         CPPUNIT_ASSERT(!str3.is_int64());
    1730        3397 :         CPPUNIT_ASSERT(str3.is_float64());
    1731        3397 :         CPPUNIT_ASSERT(str3.is_number());
    1732        3397 :         CPPUNIT_ASSERT_THROW(str3.to_int64(), as2js::exception_internal_error);
    1733        3397 :         CPPUNIT_ASSERT(close_double(str3.to_float64(), i / 1000.0, 0.00001));
    1734             : 
    1735             : #pragma GCC diagnostic push
    1736             : #pragma GCC diagnostic ignored "-Wfloat-equal"
    1737        3397 :         CPPUNIT_ASSERT(str3.is_true());
    1738             : #pragma GCC diagnostic pop
    1739        3397 :     }
    1740             : 
    1741             :     // a few more using random
    1742      100001 :     for(int i(0); i < 100000; ++i)
    1743             :     {
    1744             :         // rand generally returns 31 bit values
    1745      100000 :         int64_t value((rand() | (static_cast<uint64_t>(rand()) << 32)) ^ (static_cast<uint64_t>(rand()) << 16));
    1746      100000 :         std::stringstream str;
    1747      100000 :         str << value;
    1748      200000 :         as2js::String str1(str.str());
    1749      100000 :         CPPUNIT_ASSERT(str1.is_int64());
    1750      100000 :         CPPUNIT_ASSERT(str1.is_float64());
    1751      100000 :         CPPUNIT_ASSERT(str1.is_number());
    1752      100000 :         CPPUNIT_ASSERT(str1.to_int64() == value);
    1753      100000 :         as2js::Float64 flt1(str1.to_float64());
    1754      100000 :         as2js::Float64 flt2(static_cast<double>(value));
    1755      100000 :         CPPUNIT_ASSERT(flt1.nearly_equal(flt2, 0.0001));
    1756      100000 :         CPPUNIT_ASSERT(str1.is_true());
    1757      100000 :     }
    1758             : 
    1759             :     // test a few non-hexadecimal numbers
    1760         101 :     for(int i(0); i < 100; ++i)
    1761             :     {
    1762             :         // get a character which is not a valid hex digit and not '\0'
    1763             :         char c;
    1764         110 :         do
    1765             :         {
    1766         110 :             c = static_cast<char>(rand());
    1767             :         }
    1768             :         while(c == '\0'
    1769         109 :           || (c >= '0' && c <= '9')
    1770         104 :           || (c >= 'a' && c <= 'f')
    1771         103 :           || (c >= 'A' && c <= 'F'));
    1772             : 
    1773             :         // bad character is right at the beginning of the hex number
    1774         100 :         std::stringstream ss1;
    1775         100 :         ss1 << "0" << (rand() & 1 ? "x" : "X") << c << "123ABC";
    1776         200 :         as2js::String str1(ss1.str());
    1777         100 :         CPPUNIT_ASSERT(!str1.is_int64());
    1778         100 :         CPPUNIT_ASSERT(!str1.is_float64());
    1779         100 :         CPPUNIT_ASSERT(!str1.is_number());
    1780         100 :         CPPUNIT_ASSERT_THROW(str1.to_int64(), as2js::exception_internal_error);
    1781         100 :         CPPUNIT_ASSERT(std::isnan(str1.to_float64()));
    1782         100 :         CPPUNIT_ASSERT(str1.is_true());
    1783             : 
    1784             :         // invalid character is in the middle of the hex number
    1785         200 :         std::stringstream ss2;
    1786         100 :         ss2 << "0" << (rand() & 1 ? "x" : "X") << "123" << c << "ABC";
    1787         200 :         as2js::String str2(ss2.str());
    1788         100 :         CPPUNIT_ASSERT(!str2.is_int64());
    1789         100 :         CPPUNIT_ASSERT(!str2.is_float64());
    1790         100 :         CPPUNIT_ASSERT(!str2.is_number());
    1791         100 :         CPPUNIT_ASSERT_THROW(str2.to_int64(), as2js::exception_internal_error);
    1792         100 :         CPPUNIT_ASSERT(std::isnan(str2.to_float64()));
    1793         100 :         CPPUNIT_ASSERT(str2.is_true());
    1794         100 :     }
    1795           1 : }
    1796             : 
    1797             : 
    1798           1 : void As2JsStringUnitTests::test_concatenation()
    1799             : {
    1800             :     // this test allows us to hit the basic_string<as_char_t> constructor
    1801             :     // and copy operator
    1802             : 
    1803           1 :     as2js::String str1("blah");
    1804           2 :     as2js::String str2("foo");
    1805             : 
    1806           2 :     as2js::String str3(str1 + str2); // here!
    1807           1 :     CPPUNIT_ASSERT(str3.length() == 7);
    1808           1 :     CPPUNIT_ASSERT(str3 == "blahfoo");
    1809           1 :     CPPUNIT_ASSERT(!(str3 != "blahfoo"));
    1810           1 :     CPPUNIT_ASSERT(str3 != "blah");
    1811           1 :     CPPUNIT_ASSERT(str3 != "foo");
    1812           1 :     CPPUNIT_ASSERT(str3 == str1 + str2);
    1813           1 :     CPPUNIT_ASSERT(!(str3 != str1 + str2));
    1814             : 
    1815           2 :     as2js::String str4;
    1816           1 :     str4 = str2 + str1;
    1817           1 :     CPPUNIT_ASSERT(str4.length() == 7);
    1818           1 :     CPPUNIT_ASSERT(str4 == "fooblah");
    1819           1 :     CPPUNIT_ASSERT(!(str4 != "fooblah"));
    1820           1 :     CPPUNIT_ASSERT(str4 != "foo");
    1821           1 :     CPPUNIT_ASSERT(str4 != "blah");
    1822           2 :     CPPUNIT_ASSERT(str4 == str2 + str1);
    1823           1 : }
    1824             : 
    1825             : 
    1826           1 : void As2JsStringUnitTests::test_simplified()
    1827             : {
    1828             :     // remove spaces at the start
    1829             :     {
    1830           1 :         as2js::String str("    blah");
    1831           2 :         as2js::String simplified(str.simplified());
    1832           2 :         CPPUNIT_ASSERT(simplified == "blah");
    1833             :     }
    1834             : 
    1835             :     // remove spaces at the end
    1836             :     {
    1837           1 :         as2js::String str("blah    ");
    1838           2 :         as2js::String simplified(str.simplified());
    1839           2 :         CPPUNIT_ASSERT(simplified == "blah");
    1840             :     }
    1841             : 
    1842             :     // remove spaces at the start and end
    1843             :     {
    1844           1 :         as2js::String str("    blah    ");
    1845           2 :         as2js::String simplified(str.simplified());
    1846           2 :         CPPUNIT_ASSERT(simplified == "blah");
    1847             :     }
    1848             : 
    1849             :     // simplify spaces inside
    1850             :     {
    1851           1 :         as2js::String str("blah    foo");
    1852           2 :         as2js::String simplified(str.simplified());
    1853           2 :         CPPUNIT_ASSERT(simplified == "blah foo");
    1854             :     }
    1855             : 
    1856             :     // simplify all spaces inside
    1857             :     {
    1858           1 :         as2js::String str("    blah    foo    ");
    1859           2 :         as2js::String simplified(str.simplified());
    1860           2 :         CPPUNIT_ASSERT(simplified == "blah foo");
    1861             :     }
    1862             : 
    1863             :     // simplify spaces inside, including newlines
    1864             :     {
    1865           1 :         as2js::String str("blah  \n  foo");
    1866           2 :         as2js::String simplified(str.simplified());
    1867           2 :         CPPUNIT_ASSERT(simplified == "blah foo");
    1868             :     }
    1869             : 
    1870             :     // empty strings become zero
    1871             :     {
    1872           1 :         as2js::String str("");
    1873           2 :         as2js::String simplified(str.simplified());
    1874           2 :         CPPUNIT_ASSERT(simplified == "0");
    1875             :     }
    1876             :     {
    1877           1 :         as2js::String str("     ");
    1878           2 :         as2js::String simplified(str.simplified());
    1879           2 :         CPPUNIT_ASSERT(simplified == "0");
    1880             :     }
    1881             : 
    1882             :     // simplify to the number: just spaces around
    1883             :     {
    1884           1 :         as2js::String str("  3.14159  ");
    1885           2 :         as2js::String simplified(str.simplified());
    1886           1 :         CPPUNIT_ASSERT(simplified == "3.14159");
    1887           1 :         CPPUNIT_ASSERT(simplified.is_float64());
    1888           1 :         CPPUNIT_ASSERT(simplified.is_number());
    1889           2 :         CPPUNIT_ASSERT(as2js::Float64(simplified.to_float64()).nearly_equal(3.14159, 1e-8));
    1890             :     }
    1891             : 
    1892             :     // simplify to the number: spaces and left over
    1893             :     {
    1894           1 :         as2js::String str("  3.14159 ignore that part  ");
    1895           2 :         as2js::String simplified(str.simplified());
    1896           1 :         CPPUNIT_ASSERT(simplified == "3.14159");
    1897           1 :         CPPUNIT_ASSERT(simplified.is_float64());
    1898           1 :         CPPUNIT_ASSERT(simplified.is_number());
    1899           2 :         CPPUNIT_ASSERT(as2js::Float64(simplified.to_float64()).nearly_equal(3.14159, 1e-8));
    1900             :     }
    1901             : 
    1902             :     // simplify to the number: sign, spaces and left over
    1903             :     {
    1904           1 :         as2js::String str("  +3.14159 ignore that part  ");
    1905           2 :         as2js::String simplified(str.simplified());
    1906           1 :         CPPUNIT_ASSERT(simplified == "+3.14159");
    1907           1 :         CPPUNIT_ASSERT(simplified.is_float64());
    1908           1 :         CPPUNIT_ASSERT(simplified.is_number());
    1909           2 :         CPPUNIT_ASSERT(as2js::Float64(simplified.to_float64()).nearly_equal(3.14159, 1e-8));
    1910             :     }
    1911             :     {
    1912           1 :         as2js::String str("  -314159 ignore that part  ");
    1913           2 :         as2js::String simplified(str.simplified());
    1914           1 :         CPPUNIT_ASSERT(simplified == "-314159");
    1915           1 :         CPPUNIT_ASSERT(simplified.is_int64());
    1916           1 :         CPPUNIT_ASSERT(simplified.to_int64() == -314159);
    1917           1 :         CPPUNIT_ASSERT(simplified.is_float64());
    1918           1 :         CPPUNIT_ASSERT(simplified.is_number());
    1919           2 :         CPPUNIT_ASSERT(as2js::Float64(simplified.to_float64()).nearly_equal(-314159, 1e-8));
    1920             :     }
    1921             : 
    1922             :     // simplify to the number: sign, exponent, spaces and left over
    1923             :     {
    1924           1 :         as2js::String str("  +0.00314159e3 ignore that part  ");
    1925           2 :         as2js::String simplified(str.simplified());
    1926           1 :         CPPUNIT_ASSERT(simplified == "+0.00314159e3");
    1927           1 :         CPPUNIT_ASSERT(simplified.is_float64());
    1928           1 :         CPPUNIT_ASSERT(simplified.is_number());
    1929           2 :         CPPUNIT_ASSERT(as2js::Float64(simplified.to_float64()).nearly_equal(3.14159, 1e-8));
    1930             :     }
    1931             :     {
    1932           1 :         as2js::String str("  +0.00314159e+3 ignore that part  ");
    1933           2 :         as2js::String simplified(str.simplified());
    1934           1 :         CPPUNIT_ASSERT(simplified == "+0.00314159e+3");
    1935           1 :         CPPUNIT_ASSERT(simplified.is_float64());
    1936           1 :         CPPUNIT_ASSERT(simplified.is_number());
    1937           2 :         CPPUNIT_ASSERT(as2js::Float64(simplified.to_float64()).nearly_equal(3.14159, 1e-8));
    1938             :     }
    1939             :     {
    1940           1 :         as2js::String str("  -314159e-5 ignore that part  ");
    1941           2 :         as2js::String simplified(str.simplified());
    1942           1 :         CPPUNIT_ASSERT(simplified == "-314159");
    1943           1 :         CPPUNIT_ASSERT(simplified.is_int64());
    1944           1 :         CPPUNIT_ASSERT(simplified.to_int64() == -314159);
    1945           1 :         CPPUNIT_ASSERT(simplified.is_float64());
    1946           1 :         CPPUNIT_ASSERT(simplified.is_number());
    1947           2 :         CPPUNIT_ASSERT(as2js::Float64(simplified.to_float64()).nearly_equal(-314159, 1e-8));
    1948             :     }
    1949             :     {
    1950           1 :         as2js::String str("  -314159.e-5 ignore that part  ");
    1951           2 :         as2js::String simplified(str.simplified());
    1952           1 :         CPPUNIT_ASSERT(simplified == "-314159.e-5");
    1953           1 :         CPPUNIT_ASSERT(simplified.is_float64());
    1954           1 :         CPPUNIT_ASSERT(simplified.is_number());
    1955           2 :         CPPUNIT_ASSERT(as2js::Float64(simplified.to_float64()).nearly_equal(-3.14159, 1e-8));
    1956             :     }
    1957          13 : }
    1958             : 
    1959             : 
    1960             : 
    1961             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.10