(root)/
harfbuzz-8.3.0/
test/
api/
test-ot-tag.c
       1  /*
       2   * Copyright © 2011  Google, Inc.
       3   *
       4   *  This is part of HarfBuzz, a text shaping library.
       5   *
       6   * Permission is hereby granted, without written agreement and without
       7   * license or royalty fees, to use, copy, modify, and distribute this
       8   * software and its documentation for any purpose, provided that the
       9   * above copyright notice and the following two paragraphs appear in
      10   * all copies of this software.
      11   *
      12   * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
      13   * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
      14   * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
      15   * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
      16   * DAMAGE.
      17   *
      18   * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
      19   * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
      20   * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
      21   * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
      22   * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
      23   *
      24   * Google Author(s): Behdad Esfahbod
      25   */
      26  
      27  #include "hb-test.h"
      28  
      29  #include <hb-ot.h>
      30  
      31  /* Unit tests for hb-ot-tag.h */
      32  
      33  
      34  /* https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags */
      35  
      36  static void
      37  test_simple_tags (const char *s, hb_script_t script)
      38  {
      39    hb_script_t tag;
      40    unsigned int count = 2;
      41    hb_tag_t t[2];
      42  
      43    g_test_message ("Testing script %c%c%c%c: tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s);
      44    tag = hb_tag_from_string (s, -1);
      45  
      46    hb_ot_tags_from_script_and_language (script,
      47  				       HB_LANGUAGE_INVALID,
      48  				       &count, t, NULL, NULL);
      49  
      50    if (count)
      51      g_assert_cmphex (t[0], ==, tag);
      52    else
      53      g_assert_cmphex (HB_TAG_CHAR4 ("DFLT"), ==, tag);
      54  
      55    g_assert_cmphex (hb_ot_tag_to_script (tag), ==, script);
      56  }
      57  
      58  static void
      59  test_script_tags_from_language (const char *s, const char *lang_s, hb_script_t script)
      60  {
      61    hb_script_t tag;
      62    unsigned int count = 1;
      63    hb_tag_t t;
      64  
      65    g_test_message ("Testing script %c%c%c%c: script tag %s, language tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s, lang_s);
      66    tag = hb_tag_from_string (s, -1);
      67  
      68    hb_ot_tags_from_script_and_language (script, hb_language_from_string (lang_s, -1), &count, &t, NULL, NULL);
      69  
      70    if (count != 0)
      71    {
      72      g_assert_cmpuint (count, ==, 1);
      73      g_assert_cmphex (t, ==, tag);
      74    }
      75  }
      76  
      77  static void
      78  test_indic_tags (const char *s1, const char *s2, const char *s3, hb_script_t script)
      79  {
      80    hb_script_t tag1, tag2, tag3;
      81    hb_tag_t t[3];
      82    unsigned int count = 3;
      83  
      84    g_test_message ("Testing script %c%c%c%c: USE tag %s, new tag %s, old tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s1, s2, s3);
      85    tag1 = hb_tag_from_string (s1, -1);
      86    tag2 = hb_tag_from_string (s2, -1);
      87    tag3 = hb_tag_from_string (s3, -1);
      88  
      89    hb_ot_tags_from_script_and_language (script,
      90  				       HB_LANGUAGE_INVALID,
      91  				       &count, t, NULL, NULL);
      92  
      93    g_assert_cmpuint (count, ==, 3);
      94    g_assert_cmphex (t[0], ==, tag1);
      95    g_assert_cmphex (t[1], ==, tag2);
      96    g_assert_cmphex (t[2], ==, tag3);
      97  
      98    g_assert_cmphex (hb_ot_tag_to_script (tag1), ==, script);
      99    g_assert_cmphex (hb_ot_tag_to_script (tag2), ==, script);
     100    g_assert_cmphex (hb_ot_tag_to_script (tag3), ==, script);
     101  }
     102  
     103  static void
     104  test_ot_tag_script_degenerate (void)
     105  {
     106    hb_tag_t t[2];
     107    unsigned int count = 2;
     108  
     109    g_assert_cmphex (HB_TAG_CHAR4 ("DFLT"), ==, HB_OT_TAG_DEFAULT_SCRIPT);
     110  
     111    /* HIRAGANA and KATAKANA both map to 'kana' */
     112    test_simple_tags ("kana", HB_SCRIPT_KATAKANA);
     113  
     114    hb_ot_tags_from_script_and_language (HB_SCRIPT_HIRAGANA,
     115  				       HB_LANGUAGE_INVALID,
     116  				       &count, t, NULL, NULL);
     117  
     118    g_assert_cmpuint (count, ==, 1);
     119    g_assert_cmphex (t[0], ==, HB_TAG_CHAR4 ("kana"));
     120  
     121    test_simple_tags ("DFLT", HB_SCRIPT_INVALID);
     122  
     123    /* Spaces are replaced */
     124    g_assert_cmphex (hb_ot_tag_to_script (HB_TAG_CHAR4 ("be  ")), ==, hb_script_from_string ("Beee", -1));
     125  }
     126  
     127  static void
     128  test_ot_tag_script_simple (void)
     129  {
     130    /* Arbitrary non-existent script */
     131    test_simple_tags ("wwyz", hb_script_from_string ("wWyZ", -1));
     132  
     133    /* These we don't really care about */
     134    test_simple_tags ("zyyy", HB_SCRIPT_COMMON);
     135    test_simple_tags ("zinh", HB_SCRIPT_INHERITED);
     136    test_simple_tags ("zzzz", HB_SCRIPT_UNKNOWN);
     137  
     138    test_simple_tags ("arab", HB_SCRIPT_ARABIC);
     139    test_simple_tags ("copt", HB_SCRIPT_COPTIC);
     140    test_simple_tags ("kana", HB_SCRIPT_KATAKANA);
     141    test_simple_tags ("latn", HB_SCRIPT_LATIN);
     142  
     143    test_simple_tags ("math", HB_SCRIPT_MATH);
     144  
     145    /* These are trickier since their OT script tags have space. */
     146    test_simple_tags ("lao ", HB_SCRIPT_LAO);
     147    test_simple_tags ("yi  ", HB_SCRIPT_YI);
     148    /* Unicode-5.0 additions */
     149    test_simple_tags ("nko ", HB_SCRIPT_NKO);
     150    /* Unicode-5.1 additions */
     151    test_simple_tags ("vai ", HB_SCRIPT_VAI);
     152  
     153    /* https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags */
     154  
     155    /* Unicode-5.2 additions */
     156    test_simple_tags ("mtei", HB_SCRIPT_MEETEI_MAYEK);
     157    /* Unicode-6.0 additions */
     158    test_simple_tags ("mand", HB_SCRIPT_MANDAIC);
     159  }
     160  
     161  static void
     162  test_ot_tag_script_from_language (void)
     163  {
     164    test_script_tags_from_language (NULL, NULL, HB_SCRIPT_INVALID);
     165    test_script_tags_from_language (NULL, "en", HB_SCRIPT_INVALID);
     166    test_script_tags_from_language ("copt", "en", HB_SCRIPT_COPTIC);
     167    test_script_tags_from_language (NULL, "x-hbsc", HB_SCRIPT_INVALID);
     168    test_script_tags_from_language ("copt", "x-hbsc", HB_SCRIPT_COPTIC);
     169    test_script_tags_from_language (NULL, "x-hbsc-", HB_SCRIPT_INVALID);
     170    test_script_tags_from_language (NULL, "x-hbsc-1", HB_SCRIPT_INVALID);
     171    test_script_tags_from_language (NULL, "x-hbsc-1a", HB_SCRIPT_INVALID);
     172    test_script_tags_from_language (NULL, "x-hbsc-1a2b3c4x", HB_SCRIPT_INVALID);
     173    test_script_tags_from_language ("2lon", "x-hbsc-326c6f6e67", HB_SCRIPT_INVALID);
     174    test_script_tags_from_language ("abc ", "x-hbscabc", HB_SCRIPT_INVALID);
     175    test_script_tags_from_language ("deva", "x-hbscdeva", HB_SCRIPT_INVALID);
     176    test_script_tags_from_language ("dev2", "x-hbscdev2", HB_SCRIPT_INVALID);
     177    test_script_tags_from_language ("dev3", "x-hbscdev3", HB_SCRIPT_INVALID);
     178    test_script_tags_from_language ("dev3", "x-hbsc-64657633", HB_SCRIPT_INVALID);
     179    test_script_tags_from_language ("copt", "x-hbotpap0-hbsccopt", HB_SCRIPT_INVALID);
     180    test_script_tags_from_language (NULL, "en-x-hbsc", HB_SCRIPT_INVALID);
     181    test_script_tags_from_language ("copt", "en-x-hbsc", HB_SCRIPT_COPTIC);
     182    test_script_tags_from_language ("abc ", "en-x-hbscabc", HB_SCRIPT_INVALID);
     183    test_script_tags_from_language ("deva", "en-x-hbscdeva", HB_SCRIPT_INVALID);
     184    test_script_tags_from_language ("dev2", "en-x-hbscdev2", HB_SCRIPT_INVALID);
     185    test_script_tags_from_language ("dev3", "en-x-hbscdev3", HB_SCRIPT_INVALID);
     186    test_script_tags_from_language ("dev3", "en-x-hbsc-64657633", HB_SCRIPT_INVALID);
     187    test_script_tags_from_language ("copt", "en-x-hbotpap0-hbsccopt", HB_SCRIPT_INVALID);
     188  }
     189  
     190  static void
     191  test_ot_tag_script_indic (void)
     192  {
     193    test_indic_tags ("bng3", "bng2", "beng", HB_SCRIPT_BENGALI);
     194    test_indic_tags ("dev3", "dev2", "deva", HB_SCRIPT_DEVANAGARI);
     195    test_indic_tags ("gjr3", "gjr2", "gujr", HB_SCRIPT_GUJARATI);
     196    test_indic_tags ("gur3", "gur2", "guru", HB_SCRIPT_GURMUKHI);
     197    test_indic_tags ("knd3", "knd2", "knda", HB_SCRIPT_KANNADA);
     198    test_indic_tags ("mlm3", "mlm2", "mlym", HB_SCRIPT_MALAYALAM);
     199    test_indic_tags ("ory3", "ory2", "orya", HB_SCRIPT_ORIYA);
     200    test_indic_tags ("tml3", "tml2", "taml", HB_SCRIPT_TAMIL);
     201    test_indic_tags ("tel3", "tel2", "telu", HB_SCRIPT_TELUGU);
     202  }
     203  
     204  
     205  
     206  /* https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags */
     207  
     208  static void
     209  test_language_two_way (const char *tag_s, const char *lang_s)
     210  {
     211    hb_language_t lang = hb_language_from_string (lang_s, -1);
     212    hb_tag_t tag = hb_tag_from_string (tag_s, -1);
     213    hb_tag_t tag2;
     214    unsigned int count = 1;
     215  
     216    g_test_message ("Testing language %s <-> tag %s", lang_s, tag_s);
     217  
     218    hb_ot_tags_from_script_and_language (HB_SCRIPT_INVALID,
     219  				       lang,
     220  				       NULL, NULL, &count, &tag2);
     221  
     222    if (count)
     223      g_assert_cmphex (tag, ==, tag2);
     224    else
     225      g_assert_cmphex (tag, ==, HB_TAG_CHAR4 ("dflt"));
     226    g_assert (lang == hb_ot_tag_to_language (tag));
     227  }
     228  
     229  static void
     230  test_tag_from_language (const char *tag_s, const char *lang_s)
     231  {
     232    hb_language_t lang = hb_language_from_string (lang_s, -1);
     233    hb_tag_t tag = hb_tag_from_string (tag_s, -1);
     234    hb_tag_t tag2;
     235    unsigned int count = 1;
     236  
     237    g_test_message ("Testing language %s -> tag %s", lang_s, tag_s);
     238  
     239    hb_ot_tags_from_script_and_language (HB_SCRIPT_INVALID,
     240  				       lang,
     241  				       NULL, NULL, &count, &tag2);
     242  
     243    if (count)
     244      g_assert_cmphex (tag, ==, tag2);
     245    else
     246      g_assert_cmphex (tag, ==, HB_TAG_CHAR4 ("dflt"));
     247  }
     248  
     249  static void
     250  test_tag_to_language (const char *tag_s, const char *lang_s)
     251  {
     252    hb_language_t lang = hb_language_from_string (lang_s, -1);
     253    hb_tag_t tag = hb_tag_from_string (tag_s, -1);
     254  
     255    g_test_message ("Testing tag %s -> language %s", tag_s, lang_s);
     256  
     257    g_assert (lang == hb_ot_tag_to_language (tag));
     258  }
     259  
     260  static void
     261  test_tags_to_script_and_language (const char *script_tag_s,
     262  				  const char *lang_tag_s,
     263  				  const char *script_s,
     264  				  const char *lang_s)
     265  {
     266    hb_script_t actual_script[1];
     267    hb_language_t actual_lang[1];
     268    hb_tag_t script_tag = hb_tag_from_string (script_tag_s, -1);
     269    hb_tag_t lang_tag = hb_tag_from_string (lang_tag_s, -1);
     270    hb_ot_tags_to_script_and_language (script_tag, lang_tag, actual_script, actual_lang);
     271    g_assert_cmphex (*actual_script, ==, hb_tag_from_string (script_s, -1));
     272    g_assert_cmpstr (hb_language_to_string (*actual_lang), ==, lang_s);
     273  }
     274  
     275  static void
     276  test_ot_tags_to_script_and_language (void)
     277  {
     278    test_tags_to_script_and_language ("DFLT", "ENG", "", "en-x-hbsc-44464c54");
     279    test_tags_to_script_and_language ("latn", "ENG", "Latn", "en");
     280    test_tags_to_script_and_language ("deva", "MAR", "Deva", "mr-x-hbsc-64657661");
     281    test_tags_to_script_and_language ("dev2", "MAR", "Deva", "mr-x-hbsc-64657632");
     282    test_tags_to_script_and_language ("dev3", "MAR", "Deva", "mr");
     283    test_tags_to_script_and_language ("qaa", "QTZ0", "Qaaa", "x-hbot-51545a30-hbsc-71616120");
     284  }
     285  
     286  static void
     287  test_ot_tag_language (void)
     288  {
     289    g_assert_cmphex (HB_TAG_CHAR4 ("dflt"), ==, HB_OT_TAG_DEFAULT_LANGUAGE);
     290    test_language_two_way ("dflt", NULL);
     291  
     292    test_language_two_way ("ALT", "alt");
     293  
     294    test_language_two_way ("ARA", "ar");
     295  
     296    test_language_two_way ("AZE", "az");
     297    test_tag_from_language ("AZE", "az-ir");
     298    test_tag_from_language ("AZE", "az-az");
     299  
     300    test_language_two_way ("ENG", "en");
     301    test_tag_from_language ("ENG", "en_US");
     302  
     303    test_language_two_way ("CJA", "cja-x-hbot-434a4120"); /* Western Cham */
     304    test_language_two_way ("CJM", "cjm-x-hbot-434a4d20"); /* Eastern Cham */
     305    test_tag_from_language ("CJM", "cjm");
     306    test_language_two_way ("EVN", "eve");
     307  
     308    test_language_two_way ("HAL", "cfm"); /* BCP47 and current ISO639-3 code for Halam/Falam Chin */
     309    test_tag_from_language ("HAL", "flm"); /* Retired ISO639-3 code for Halam/Falam Chin */
     310  
     311    test_language_two_way ("HYE0", "hy");
     312    test_language_two_way ("HYE", "hyw");
     313  
     314    test_tag_from_language ("QIN", "bgr"); /* Bawm Chin */
     315    test_tag_from_language ("QIN", "cbl"); /* Bualkhaw Chin */
     316    test_tag_from_language ("QIN", "cka"); /* Khumi Awa Chin */
     317    test_tag_from_language ("QIN", "cmr"); /* Mro-Khimi Chin */
     318    test_tag_from_language ("QIN", "cnb"); /* Chinbon Chin */
     319    test_tag_from_language ("QIN", "cnh"); /* Hakha Chin */
     320    test_tag_from_language ("QIN", "cnk"); /* Khumi Chin */
     321    test_tag_from_language ("QIN", "cnw"); /* Ngawn Chin */
     322    test_tag_from_language ("QIN", "csh"); /* Asho Chin */
     323    test_tag_from_language ("QIN", "csy"); /* Siyin Chin */
     324    test_tag_from_language ("QIN", "ctd"); /* Tedim Chin */
     325    test_tag_from_language ("QIN", "czt"); /* Zotung Chin */
     326    test_tag_from_language ("QIN", "dao"); /* Daai Chin */
     327    test_tag_from_language ("QIN", "hlt"); /* Matu Chin */
     328    test_tag_from_language ("QIN", "mrh"); /* Mara Chin */
     329    test_tag_from_language ("QIN", "pck"); /* Paite Chin */
     330    test_tag_from_language ("QIN", "sez"); /* Senthang Chin */
     331    test_tag_from_language ("QIN", "tcp"); /* Tawr Chin */
     332    test_tag_from_language ("QIN", "tcz"); /* Thado Chin */
     333    test_tag_from_language ("QIN", "yos"); /* Yos, deprecated by IANA in favor of Zou [zom] */
     334    test_tag_from_language ("QIN", "zom"); /* Zou */
     335    test_tag_to_language ("QIN", "bgr");   /* no single BCP47 tag for Chin; picking Bawm Chin */
     336  
     337    test_language_two_way ("FAR", "fa");
     338    test_tag_from_language ("FAR", "fa_IR");
     339  
     340    test_language_two_way ("MNK", "man"); /* Mandingo [macrolanguage] */
     341  
     342    test_language_two_way ("SWA", "aii"); /* Swadaya Aramaic */
     343  
     344    test_language_two_way ("SYR", "syr"); /* Syriac [macrolanguage] */
     345    test_tag_from_language ("SYR", "amw"); /* Western Neo-Aramaic */
     346    test_tag_from_language ("SYR", "cld"); /* Chaldean Neo-Aramaic */
     347    test_tag_from_language ("SYR", "syc"); /* Classical Syriac */
     348  
     349    test_language_two_way ("TUA", "tru"); /* Turoyo Aramaic */
     350  
     351    test_tag_from_language ("ZHS", "zh"); /* Chinese */
     352    test_tag_from_language ("ZHS", "zh-cn"); /* Chinese (China) */
     353    test_tag_from_language ("ZHS", "zh-sg"); /* Chinese (Singapore) */
     354    test_tag_from_language ("ZHTM", "zh-mo"); /* Chinese (Macao) */
     355    test_tag_from_language ("ZHTM", "zh-hant-mo"); /* Chinese (Macao) */
     356    test_tag_from_language ("ZHS", "zh-hans-mo"); /* Chinese (Simplified, Macao) */
     357    test_language_two_way ("ZHH", "zh-HK"); /* Chinese (Hong Kong) */
     358    test_tag_from_language ("ZHH", "zH-HanT-hK"); /* Chinese (Hong Kong) */
     359    test_tag_from_language ("ZHS", "zH-HanS-hK"); /* Chinese (Simplified, Hong Kong) */
     360    test_tag_from_language ("ZHT", "zh-tw"); /* Chinese (Taiwan) */
     361    test_language_two_way ("ZHS", "zh-Hans"); /* Chinese (Simplified) */
     362    test_language_two_way ("ZHT", "zh-Hant"); /* Chinese (Traditional) */
     363    test_tag_from_language ("ZHS", "zh-xx"); /* Chinese (Other) */
     364  
     365    test_tag_from_language ("ZHS", "zh-Hans-TW");
     366  
     367    test_tag_from_language ("ZHH", "yue");
     368    test_tag_from_language ("ZHH", "yue-Hant");
     369    test_tag_from_language ("ZHS", "yue-Hans");
     370  
     371    test_language_two_way ("ABC", "abc-x-hbot-41424320");
     372    test_language_two_way ("ABCD", "x-hbot-41424344");
     373    test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbotabc-zxc");
     374    test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbotabc");
     375    test_tag_from_language ("ABCD", "asdf-asdf-wer-x-hbotabcd");
     376    test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbot-41424320-zxc");
     377    test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbot-41424320");
     378    test_tag_from_language ("ABCD", "asdf-asdf-wer-x-hbot-41424344");
     379  
     380    test_tag_from_language ("dflt", "asdf-asdf-wer-x-hbot");
     381    test_tag_from_language ("dflt", "asdf-asdf-wer-x-hbot-zxc");
     382    test_tag_from_language ("dflt", "asdf-asdf-wer-x-hbot-zxc-414243");
     383    test_tag_from_language ("dflt", "asdf-asdf-wer-x-hbot-414243");
     384    test_tag_from_language ("dflt", "asdf-asdf-wer-x-hbot-4142432");
     385  
     386    test_tag_from_language ("dflt", "xy");
     387    test_tag_from_language ("XYZ", "xyz"); /* Unknown ISO 639-3 */
     388    test_tag_from_language ("XYZ", "xyz-qw"); /* Unknown ISO 639-3 */
     389  
     390    /*
     391     * Invalid input. The precise answer does not matter, as long as it
     392     * does not crash or get into an infinite loop.
     393     */
     394    test_tag_from_language ("IPPH", "-fonipa");
     395  
     396    /*
     397     * Tags that contain "-fonipa" as a substring but which do not contain
     398     * the subtag "fonipa".
     399     */
     400    test_tag_from_language ("ENG", "en-fonipax");
     401    test_tag_from_language ("ENG", "en-x-fonipa");
     402    test_tag_from_language ("ENG", "en-a-fonipa");
     403    test_tag_from_language ("ENG", "en-a-qwe-b-fonipa");
     404  
     405    /* International Phonetic Alphabet */
     406    test_tag_from_language ("IPPH", "en-fonipa");
     407    test_tag_from_language ("IPPH", "en-fonipax-fonipa");
     408    test_tag_from_language ("IPPH", "rm-CH-fonipa-sursilv-x-foobar");
     409    test_language_two_way ("IPPH", "und-fonipa");
     410    test_tag_from_language ("IPPH", "zh-fonipa");
     411  
     412    /* North American Phonetic Alphabet (Americanist Phonetic Notation) */
     413    test_tag_from_language ("APPH", "en-fonnapa");
     414    test_tag_from_language ("APPH", "chr-fonnapa");
     415    test_language_two_way ("APPH", "und-fonnapa");
     416  
     417    /* Khutsuri Georgian */
     418    test_tag_from_language ("KGE", "ka-Geok");
     419    test_language_two_way ("KGE", "und-Geok");
     420  
     421    /* Irish Traditional */
     422    test_language_two_way ("IRT", "ga-Latg");
     423  
     424    /* Moldavian */
     425    test_language_two_way ("MOL", "ro-MD");
     426  
     427    /* Polytonic Greek */
     428    test_language_two_way ("PGR", "el-polyton");
     429    test_tag_from_language ("PGR", "el-CY-polyton");
     430  
     431    /* Estrangela Syriac */
     432    test_tag_from_language ("SYRE", "aii-Syre");
     433    test_tag_from_language ("SYRE", "de-Syre");
     434    test_tag_from_language ("SYRE", "syr-Syre");
     435    test_language_two_way ("SYRE", "und-Syre");
     436  
     437    /* Western Syriac */
     438    test_tag_from_language ("SYRJ", "aii-Syrj");
     439    test_tag_from_language ("SYRJ", "de-Syrj");
     440    test_tag_from_language ("SYRJ", "syr-Syrj");
     441    test_language_two_way ("SYRJ", "und-Syrj");
     442  
     443    /* Eastern Syriac */
     444    test_tag_from_language ("SYRN", "aii-Syrn");
     445    test_tag_from_language ("SYRN", "de-Syrn");
     446    test_tag_from_language ("SYRN", "syr-Syrn");
     447    test_language_two_way ("SYRN", "und-Syrn");
     448  
     449    /* Test that x-hbot overrides the base language */
     450    test_tag_from_language ("ABC", "fa-x-hbotabc-hbot-41686121-zxc");
     451    test_tag_from_language ("ABC", "fa-ir-x-hbotabc-hbot-41686121-zxc");
     452    test_tag_from_language ("ABC", "zh-x-hbotabc-hbot-41686121-zxc");
     453    test_tag_from_language ("ABC", "zh-cn-x-hbotabc-hbot-41686121-zxc");
     454    test_tag_from_language ("ABC", "zh-xy-x-hbotabc-hbot-41686121-zxc");
     455    test_tag_from_language ("ABC", "xyz-xy-x-hbotabc-hbot-41686121-zxc");
     456  
     457    test_tag_from_language ("Aha!", "fa-x-hbot-41686121-hbotabc-zxc");
     458    test_tag_from_language ("Aha!", "fa-ir-x-hbot-41686121-hbotabc-zxc");
     459    test_tag_from_language ("Aha!", "zh-x-hbot-41686121-hbotabc-zxc");
     460    test_tag_from_language ("Aha!", "zh-cn-x-hbot-41686121-hbotabc-zxc");
     461    test_tag_from_language ("Aha!", "zh-xy-x-hbot-41686121-hbotabc-zxc");
     462    test_tag_from_language ("Aha!", "xyz-xy-x-hbot-41686121-hbotabc-zxc");
     463  
     464    /* Invalid x-hbot */
     465    test_tag_from_language ("dflt", "x-hbot");
     466    test_tag_from_language ("dflt", "x-hbot-");
     467    test_tag_from_language ("dflt", "x-hbot-1");
     468    test_tag_from_language ("dflt", "x-hbot-1a");
     469    test_tag_from_language ("dflt", "x-hbot-1a2b3c4x");
     470    test_tag_from_language ("2lon", "x-hbot-326c6f6e67");
     471  
     472    /* Unnormalized BCP 47 tags */
     473    test_tag_from_language ("ARA", "ar-aao");
     474    test_tag_from_language ("JBO", "art-lojban");
     475    test_tag_from_language ("KOK", "kok-gom");
     476    test_tag_from_language ("LTZ", "i-lux");
     477    test_tag_from_language ("MNG", "drh");
     478    test_tag_from_language ("MOR", "ar-ary");
     479    test_tag_from_language ("MOR", "ar-ary-DZ");
     480    test_tag_from_language ("NOR", "no-bok");
     481    test_tag_from_language ("NYN", "no-nyn");
     482    test_tag_from_language ("ZHS", "i-hak");
     483    test_tag_from_language ("ZHS", "zh-guoyu");
     484    test_tag_from_language ("ZHS", "zh-min");
     485    test_tag_from_language ("ZHS", "zh-min-nan");
     486    test_tag_from_language ("ZHS", "zh-xiang");
     487  
     488    /* BCP 47 tags that look similar to unrelated language system tags */
     489    test_tag_from_language ("SQI", "als");
     490    test_tag_from_language ("dflt", "far");
     491  
     492    /* A UN M.49 region code, not an extended language subtag */
     493    test_tag_from_language ("ARA", "ar-001");
     494  
     495    /* An invalid tag */
     496    test_tag_from_language ("TRK", "tr@foo=bar");
     497  }
     498  
     499  static void
     500  test_tags (hb_script_t  script,
     501  	   const char  *lang_s,
     502  	   unsigned int script_count,
     503  	   unsigned int language_count,
     504  	   unsigned int expected_script_count,
     505  	   unsigned int expected_language_count,
     506  	   ...)
     507  {
     508    va_list expected_tags;
     509    unsigned int i;
     510    hb_tag_t *script_tags = malloc (script_count * sizeof (hb_tag_t));
     511    hb_tag_t *language_tags = malloc (language_count * sizeof (hb_tag_t));
     512    hb_language_t lang;
     513    g_assert (script_tags);
     514    g_assert (language_tags);
     515    lang = hb_language_from_string (lang_s, -1);
     516    va_start (expected_tags, expected_language_count);
     517  
     518    hb_ot_tags_from_script_and_language (script, lang, &script_count, script_tags, &language_count, language_tags);
     519  
     520    g_assert_cmpuint (script_count, ==, expected_script_count);
     521    g_assert_cmpuint (language_count, ==, expected_language_count);
     522  
     523    for (i = 0; i < script_count + language_count; i++)
     524    {
     525      hb_tag_t expected_tag = hb_tag_from_string (va_arg (expected_tags, const char *), -1);
     526      hb_tag_t actual_tag = i < script_count ? script_tags[i] : language_tags[i - script_count];
     527      g_assert_cmphex (actual_tag, ==, expected_tag);
     528    }
     529  
     530    free (script_tags);
     531    free (language_tags);
     532    va_end (expected_tags);
     533  }
     534  
     535  static void
     536  test_ot_tag_full (void)
     537  {
     538    test_tags (HB_SCRIPT_INVALID, "en", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "ENG");
     539    test_tags (HB_SCRIPT_INVALID, "en-x-hbscdflt", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "DFLT", "ENG");
     540    test_tags (HB_SCRIPT_LATIN, "en", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "latn", "ENG");
     541    test_tags (HB_SCRIPT_LATIN, "en", 0, 0, 0, 0);
     542    test_tags (HB_SCRIPT_INVALID, "und-fonnapa", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "APPH");
     543    test_tags (HB_SCRIPT_INVALID, "en-fonnapa", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "APPH");
     544    test_tags (HB_SCRIPT_INVALID, "x-hbot1234-hbsc5678", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "5678", "1234");
     545    test_tags (HB_SCRIPT_INVALID, "x-hbsc5678-hbot1234", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "5678", "1234");
     546    test_tags (HB_SCRIPT_MALAYALAM, "ml", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 3, 2, "mlm3", "mlm2", "mlym", "MAL", "MLR");
     547    test_tags (HB_SCRIPT_MALAYALAM, "ml", 1, 1, 1, 1, "mlm3", "MAL");
     548    test_tags (HB_SCRIPT_MYANMAR, "und", HB_OT_MAX_TAGS_PER_SCRIPT, 0, 2, 0, "mym2", "mymr");
     549    test_tags (HB_SCRIPT_INVALID, "xyz", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "XYZ");
     550    test_tags (HB_SCRIPT_INVALID, "xy", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 0);
     551  }
     552  
     553  int
     554  main (int argc, char **argv)
     555  {
     556    hb_test_init (&argc, &argv);
     557  
     558    hb_test_add (test_ot_tag_script_degenerate);
     559    hb_test_add (test_ot_tag_script_simple);
     560    hb_test_add (test_ot_tag_script_from_language);
     561    hb_test_add (test_ot_tag_script_indic);
     562  
     563    hb_test_add (test_ot_tags_to_script_and_language);
     564  
     565    hb_test_add (test_ot_tag_language);
     566  
     567    hb_test_add (test_ot_tag_full);
     568  
     569    return hb_test_run();
     570  }