text: /*
   * Copyright 2013 MongoDB, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include <bson.h>
  
  #include <assert.h>
  
  #include "bson-tests.h"
  #include "TestSuite.h"
  
  
  static void
  test_bson_utf8_validate (void)
  {
     static const unsigned char test1[] = {
        0xe2, 0x82, 0xac, ' ', 0xe2, 0x82, 0xac, ' ', 0xe2, 0x82, 0xac, 0};
     static const unsigned char test2[] = {0xc0, 0x80, 0};
  
     assert (bson_utf8_validate ("asdf", 4, false));
     assert (bson_utf8_validate ("asdf", 4, true));
     assert (bson_utf8_validate ("asdf", 5, true));
     assert (!bson_utf8_validate ("asdf", 5, false));
  
     assert (bson_utf8_validate ((const char *) test1, 11, true));
     assert (bson_utf8_validate ((const char *) test1, 11, false));
     assert (bson_utf8_validate ((const char *) test1, 12, true));
     assert (!bson_utf8_validate ((const char *) test1, 12, false));
  
     assert (bson_utf8_validate ((const char *) test2, 2, true));
  }
  
  
  static void
  test_bson_utf8_escape_for_json (void)
  {
     char *str;
  
     str = bson_utf8_escape_for_json ("my\0key", 6);
     assert (0 == memcmp (str, "my\\u0000key", 7));
     bson_free (str);
  
     str = bson_utf8_escape_for_json ("my\"key", 6);
     assert (0 == memcmp (str, "my\\\"key", 8));
     bson_free (str);
  
     str = bson_utf8_escape_for_json ("my\\key", 6);
     assert (0 == memcmp (str, "my\\\\key", 8));
     bson_free (str);
  
     str = bson_utf8_escape_for_json ("\\\"\\\"", -1);
     assert (0 == memcmp (str, "\\\\\\\"\\\\\\\"", 9));
     bson_free (str);
  }
  
  
  static void
  test_bson_utf8_invalid (void)
  {
     /* no UTF-8 sequence can start with 0x80 */
     static const unsigned char bad[] = {0x80, 0};
  
     assert (!bson_utf8_validate ((const char *) bad, 1, true));
     assert (!bson_utf8_validate ((const char *) bad, 1, false));
     assert (!bson_utf8_escape_for_json ((const char *) bad, 1));
  }
  
  
  static void
  test_bson_utf8_nil (void)
  {
     static const unsigned char test[] = {'a', 0, 'b', 0};
     char *str;
  
     assert (bson_utf8_validate ((const char *) test, 3, true));
     assert (!bson_utf8_validate ((const char *) test, 3, false));
  
     /* no length provided, stop at first nil */
     str = bson_utf8_escape_for_json ((const char *) test, -1);
     ASSERT_CMPSTR ("a", str);
     bson_free (str);
  
     str = bson_utf8_escape_for_json ((const char *) test, 3);
     ASSERT_CMPSTR ("a\\u0000b", str);
     bson_free (str);
  }
  
  
  static void
  test_bson_utf8_get_char (void)
  {
     static const char *test1 = "asdf";
     static const unsigned char test2[] = {
        0xe2, 0x82, 0xac, ' ', 0xe2, 0x82, 0xac, ' ', 0xe2, 0x82, 0xac, 0};
     const char *c;
  
     c = test1;
     assert (bson_utf8_get_char (c) == 'a');
     c = bson_utf8_next_char (c);
     assert (bson_utf8_get_char (c) == 's');
     c = bson_utf8_next_char (c);
     assert (bson_utf8_get_char (c) == 'd');
     c = bson_utf8_next_char (c);
     assert (bson_utf8_get_char (c) == 'f');
     c = bson_utf8_next_char (c);
     assert (!*c);
  
     c = (const char *) test2;
     assert (bson_utf8_get_char (c) == 0x20AC);
     c = bson_utf8_next_char (c);
     assert (c == (const char *) test2 + 3);
     assert (bson_utf8_get_char (c) == ' ');
     c = bson_utf8_next_char (c);
     assert (bson_utf8_get_char (c) == 0x20AC);
     c = bson_utf8_next_char (c);
     assert (bson_utf8_get_char (c) == ' ');
     c = bson_utf8_next_char (c);
     assert (bson_utf8_get_char (c) == 0x20AC);
     c = bson_utf8_next_char (c);
     assert (!*c);
  }
  
  
  static void
  test_bson_utf8_from_unichar (void)
  {
     static const char test1[] = {'a'};
     static const unsigned char test2[] = {0xc3, 0xbf};
     static const unsigned char test3[] = {0xe2, 0x82, 0xac};
     uint32_t len;
     char str[6];
  
     /*
      * First possible sequence of a certain length.
      */
     bson_utf8_from_unichar (0, str, &len);
     assert (len == 1);
     bson_utf8_from_unichar (0x00000080, str, &len);
     assert (len == 2);
     bson_utf8_from_unichar (0x00000800, str, &len);
     assert (len == 3);
     bson_utf8_from_unichar (0x00010000, str, &len);
     assert (len == 4);
     bson_utf8_from_unichar (0x00200000, str, &len);
     assert (len == 5);
     bson_utf8_from_unichar (0x04000000, str, &len);
     assert (len == 6);
  
     /*
      * Last possible sequence of a certain length.
      */
     bson_utf8_from_unichar (0x0000007F, str, &len);
     assert (len == 1);
     bson_utf8_from_unichar (0x000007FF, str, &len);
     assert (len == 2);
     bson_utf8_from_unichar (0x0000FFFF, str, &len);
     assert (len == 3);
     bson_utf8_from_unichar (0x001FFFFF, str, &len);
     assert (len == 4);
     bson_utf8_from_unichar (0x03FFFFFF, str, &len);
     assert (len == 5);
     bson_utf8_from_unichar (0x7FFFFFFF, str, &len);
     assert (len == 6);
  
     /*
      * Other interesting values.
      */
     bson_utf8_from_unichar (0x0000D7FF, str, &len);
     assert (len == 3);
     bson_utf8_from_unichar (0x0000E000, str, &len);
     assert (len == 3);
     bson_utf8_from_unichar (0x0000FFFD, str, &len);
     assert (len == 3);
     bson_utf8_from_unichar (0x0010FFFF, str, &len);
     assert (len == 4);
     bson_utf8_from_unichar (0x00110000, str, &len);
     assert (len == 4);
  
     bson_utf8_from_unichar ('a', str, &len);
     assert (len == 1);
     assert (!memcmp (test1, str, 1));
  
     bson_utf8_from_unichar (0xFF, str, &len);
     assert (len == 2);
     assert (!memcmp ((const char *) test2, str, 2));
  
     bson_utf8_from_unichar (0x20AC, str, &len);
     assert (len == 3);
     assert (!memcmp ((const char *) test3, str, 3));
  }
  
  
  static void
  test_bson_utf8_non_shortest (void)
  {
     static const char *tests[] = {
        "\xE0\x80\x80",     /* Non-shortest form representation of U+0000 */
        "\xF0\x80\x80\x80", /* Non-shortest form representation of U+0000 */
  
        "\xE0\x83\xBF",     /* Non-shortest form representation of U+00FF */
        "\xF0\x80\x83\xBF", /* Non-shortest form representation of U+00FF */
  
        "\xF0\x80\xA3\x80", /* Non-shortest form representation of U+08C0 */
  
        NULL};
     static const char *valid_tests[] = {
        "\xC0\x80", /* Non-shortest form representation of U+0000.
                     * This is how \0 should be encoded. Special casing
                     * to allow for use in things like strlen(). */
  
        NULL};
     int i;
  
     for (i = 0; tests[i]; i++) {
        if (bson_utf8_validate (tests[i], strlen (tests[i]), false)) {
           fprintf (stderr, "non-shortest form failure, test %d\n", i);
           assert (false);
        }
     }
  
     for (i = 0; valid_tests[i]; i++) {
        if (!bson_utf8_validate (
               valid_tests[i], strlen (valid_tests[i]), false)) {
           fprintf (stderr, "non-shortest form failure, valid_tests %d\n", i);
           assert (false);
        }
     }
  }
  
  
  void
  test_utf8_install (TestSuite *suite)
  {
     TestSuite_Add (suite, "/bson/utf8/validate", test_bson_utf8_validate);
     TestSuite_Add (suite, "/bson/utf8/invalid", test_bson_utf8_invalid);
     TestSuite_Add (suite, "/bson/utf8/nil", test_bson_utf8_nil);
     TestSuite_Add (
        suite, "/bson/utf8/escape_for_json", test_bson_utf8_escape_for_json);
     TestSuite_Add (
        suite, "/bson/utf8/get_char_next_char", test_bson_utf8_get_char);
     TestSuite_Add (
        suite, "/bson/utf8/from_unichar", test_bson_utf8_from_unichar);
     TestSuite_Add (
        suite, "/bson/utf8/non_shortest", test_bson_utf8_non_shortest);
  }
  