#include <stdio.h>
#include <string.h>
#include "lingenic_text.h"
static int failures = 0;
static int checks = 0;
#define CHECK(cond) do { \
checks++; \
if (!(cond)) { \
failures++; \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
} \
} while (0)
int main (int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "usage: %s <ucd dir>\n", argv[0]);
return 2;
}
lingenic_text_cinit();
CHECK(lt_initialized() == 0);
CHECK(lt_init(argv[1]) == 0);
CHECK(lt_initialized() == 1);
{
const uint8_t eacute[] = { 0xC3, 0xA9 };
uint32_t cp = 0; size_t sl = 0;
CHECK(lt_utf8_decode(eacute, 2, 0, &cp, &sl) == 1);
CHECK(cp == 0xE9 && sl == 2);
const uint8_t bad[] = { 0xFF };
CHECK(lt_utf8_decode(bad, 1, 0, &cp, &sl) == 0);
CHECK(sl == 1);
uint8_t enc[4]; size_t el = 0;
CHECK(lt_utf8_encode(0xE9, enc, 4, &el) == 0);
CHECK(el == 2 && enc[0] == 0xC3 && enc[1] == 0xA9);
CHECK(lt_utf8_encode(0xD800, enc, 4, &el) == LT_ERR);
}
{
const uint8_t g[] = { 'e', 0xCC, 0x81 };
size_t np = 0;
CHECK(lt_next_grapheme_break(g, 3, 0, &np) == 0);
CHECK(np == 3);
const uint8_t w[] = "hello world";
CHECK(lt_next_word_break(w, 11, 0, &np) == 0);
CHECK(np == 5);
const uint8_t s[] = "One. Two.";
CHECK(lt_next_sentence_break(s, 9, 0, &np) == 0);
CHECK(np == 5);
const uint8_t l[] = "aa bb";
CHECK(lt_next_line_break(l, 5, 0, &np) == 0);
CHECK(np > 0 && np <= 5);
}
{
const uint8_t decomp[] = { 'e', 0xCC, 0x81 };
uint8_t out[16]; size_t ol = 0;
CHECK(lt_normalize(LT_NFC, decomp, 3, out, 16, &ol) == 0);
CHECK(ol == 2 && out[0] == 0xC3 && out[1] == 0xA9);
const uint8_t comp[] = { 0xC3, 0xA9 };
CHECK(lt_normalize(LT_NFD, comp, 2, out, 16, &ol) == 0);
CHECK(ol == 3 && out[0] == 'e' && out[1] == 0xCC && out[2] == 0x81);
CHECK(lt_quick_check(LT_NFC, comp, 2) == LT_QC_YES);
CHECK(lt_is_normalized(LT_NFC, comp, 2) == 1);
CHECK(lt_is_normalized(LT_NFC, decomp, 3) == 0);
CHECK(lt_ccc(0x301) == 230);
CHECK(lt_ccc('A') == 0);
}
{
uint8_t out[16]; size_t ol = 0;
const uint8_t sharp_s[] = { 0xC3, 0x9F };
CHECK(lt_uppercase(sharp_s, 2, out, 16, &ol) == 0);
CHECK(ol == 2 && out[0] == 'S' && out[1] == 'S');
const uint8_t abc[] = "ABC";
CHECK(lt_lowercase(abc, 3, out, 16, &ol) == 0);
CHECK(ol == 3 && memcmp(out, "abc", 3) == 0);
const uint8_t hello[] = "hello";
CHECK(lt_titlecase(hello, 5, out, 16, &ol) == 0);
CHECK(ol == 5 && memcmp(out, "Hello", 5) == 0);
const uint8_t mixed[] = "AbC";
CHECK(lt_casefold(mixed, 3, out, 16, &ol) == 0);
CHECK(ol == 3 && memcmp(out, "abc", 3) == 0);
CHECK(lt_uppercase(sharp_s, 2, out, 1, &ol) == 1);
CHECK(lt_is_cased('A') == 1);
CHECK(lt_is_cased('1') == 0);
CHECK(lt_is_case_ignorable('\'') == 1);
}
{
int r = 99;
CHECK(lt_collate((const uint8_t*)"a", 1, (const uint8_t*)"b", 1,
LT_COLLATE_NON_IGNORABLE, &r) == 0);
CHECK(r == -1);
CHECK(lt_collate((const uint8_t*)"a", 1, (const uint8_t*)"a", 1,
LT_COLLATE_SHIFTED, &r) == 0);
CHECK(r == 0);
uint8_t ka[64], kb[64]; size_t la = 0, lb = 0;
CHECK(lt_sort_key((const uint8_t*)"a", 1, LT_COLLATE_NON_IGNORABLE,
ka, 64, &la) == 0);
CHECK(lt_sort_key((const uint8_t*)"b", 1, LT_COLLATE_NON_IGNORABLE,
kb, 64, &lb) == 0);
size_t m = la < lb ? la : lb;
int cmp = memcmp(ka, kb, m);
CHECK(cmp < 0 || (cmp == 0 && la < lb));
}
{
const uint8_t ltr[] = "abc";
uint8_t levels[8]; size_t ncp = 0; uint8_t pl = 9;
CHECK(lt_bidi_resolve(ltr, 3, LT_DIR_AUTO, levels, 8, &ncp, &pl) == 0);
CHECK(ncp == 3 && pl == 0);
CHECK(levels[0] == 0 && levels[1] == 0 && levels[2] == 0);
uint32_t order[8];
CHECK(lt_bidi_reorder(levels, 3, pl, order, 8) == 0);
CHECK(order[0] == 0 && order[1] == 1 && order[2] == 2);
const uint8_t heb[] = { 0xD7, 0x90, 0xD7, 0x91, 0xD7, 0x92 };
CHECK(lt_bidi_resolve(heb, 6, LT_DIR_AUTO, levels, 8, &ncp, &pl) == 0);
CHECK(ncp == 3 && pl == 1);
CHECK(lt_bidi_reorder(levels, 3, pl, order, 8) == 0);
CHECK(order[0] == 2 && order[1] == 1 && order[2] == 0);
CHECK(lt_bidi_needs_mirror('(', 1) == 1);
CHECK(lt_bidi_needs_mirror('(', 0) == 0);
CHECK(lt_bidi_mirrored('(') == 1);
CHECK(lt_bidi_mirrored('A') == 0);
}
CHECK(lt_display_width('A') == 1);
CHECK(lt_display_width(0x4E00) == 2);
CHECK(lt_east_asian_width(0x4E00) == LT_EAW_WIDE);
CHECK(lt_east_asian_width('A') == LT_EAW_NARROW);
{
CHECK(lt_is_emoji(0x1F600) == 1);
CHECK(lt_is_emoji('A') == 0);
CHECK(lt_is_emoji_modifier(0x1F3FB) == 1);
CHECK(lt_is_emoji_component(0x20E3) == 1);
CHECK(lt_extended_pictographic(0x1F600) == 1);
const uint8_t grin[] = { 0xF0, 0x9F, 0x98, 0x80 };
CHECK(lt_emoji_classify(grin, 4) == LT_EMOJI_CHARACTER);
const uint8_t flag[] = { 0xF0, 0x9F, 0x87, 0xBA, 0xF0, 0x9F, 0x87, 0xB8 };
CHECK(lt_emoji_classify(flag, 8) == LT_EMOJI_FLAG_SEQ);
CHECK(lt_emoji_classify((const uint8_t*)"hi", 2) == LT_EMOJI_NOT_EMOJI);
}
CHECK(lt_is_identifier_start('A') == 1);
CHECK(lt_is_identifier_start('1') == 0);
CHECK(lt_is_identifier_part('1') == 1);
CHECK(lt_is_identifier_part(' ') == 0);
{
const uint8_t dom[] = { 'b', 0xC3, 0xBC, 'c', 'h', 'e', 'r',
'.', 'd', 'e' };
uint8_t out[256]; size_t ol = 0;
CHECK(lt_idna_to_ascii(dom, 10, LT_IDNA_DEFAULT, out, 256, &ol)
== LT_IDNA_SUCCESS);
CHECK(ol == 16 && memcmp(out, "xn--bcher-kva.de", 16) == 0);
const uint8_t ace[] = "xn--bcher-kva.de";
CHECK(lt_idna_to_unicode(ace, 16, LT_IDNA_DEFAULT, out, 256, &ol)
== LT_IDNA_SUCCESS);
CHECK(ol == 10 && memcmp(out, dom, 10) == 0);
}
{
char name[64];
int scr = lt_script('A');
CHECK(scr >= 0);
CHECK(lt_script_count() > 0 && scr < lt_script_count());
CHECK(lt_script_name(scr, name, 64) == 5);
CHECK(strcmp(name, "Latin") == 0);
CHECK(lt_in_script_extensions('A', scr) == 1);
CHECK(lt_script_extension_count('A') >= 1);
CHECK(lt_script_extension('A', 0) >= 0);
int gc = lt_general_category('A');
CHECK(gc >= 0);
CHECK(lt_general_category_count() > 0 && gc < lt_general_category_count());
CHECK(lt_general_category_name(gc, name, 64) == 2);
CHECK(strcmp(name, "Lu") == 0);
CHECK(lt_grapheme_break_property(0x0D) == LT_GBP_CR);
CHECK(lt_grapheme_break_property(0x200D) == LT_GBP_ZWJ);
CHECK(lt_word_break_property('a') == LT_WBP_ALETTER);
CHECK(lt_sentence_break_property('a') == LT_SBP_LOWER);
CHECK(lt_line_break_property(0x0A) == LT_LBP_LF);
CHECK(lt_line_break_property(' ') == LT_LBP_SP);
CHECK(lt_bidi_class('A') == LT_BC_L);
CHECK(lt_bidi_class(0x05D0) == LT_BC_R);
CHECK(lt_joining_type(0x0628) == LT_JT_D);
CHECK(lt_joining_type('A') == LT_JT_U);
CHECK(lt_indic_conjunct_break(0x094D) == LT_INCB_LINKER);
CHECK(lt_indic_conjunct_break('A') == LT_INCB_NONE);
}
CHECK(lt_utf8_decode(NULL, 1, 0, NULL, NULL) == LT_ERR);
CHECK(lt_normalize(7, (const uint8_t*)"a", 1, NULL, 0, NULL) == LT_ERR);
CHECK(lt_ccc(0x110000) == LT_ERR);
CHECK(lt_display_width(0xFFFFFFFFu) == LT_ERR);
lingenic_text_cfinal();
printf("%d checks, %d failures\n", checks, failures);
return failures == 0 ? 0 : 1;
}