This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Make XS::APItest::establish_cleanup protect existing stacks
[perl5.git] / ext / XS-APItest / APItest.xs
index 1d8a551..1b8ec3f 100644 (file)
@@ -2,6 +2,7 @@
 #include "EXTERN.h"
 #include "perl.h"
 #include "XSUB.h"
+#include "fakesdio.h"   /* Causes us to use PerlIO below */
 
 typedef SV *SVREF;
 typedef PTR_TBL_t *XS__APItest__PtrTable;
@@ -78,7 +79,7 @@ bool sv_setsv_cow_hashkey_notcore(void);
 /* A routine to test hv_delayfree_ent
    (which itself is tested by testing on hv_free_ent  */
 
-typedef void (freeent_function)(pTHX_ HV *, register HE *);
+typedef void (freeent_function)(pTHX_ HV *, HE *);
 
 void
 test_freeent(freeent_function *f) {
@@ -148,8 +149,8 @@ bitflip_key(pTHX_ IV action, SV *field) {
                const char *const end = p + len;
                while (p < end) {
                    STRLEN len;
-                   UV chr = utf8_to_uvuni((U8 *)p, &len);
-                   new_p = (char *)uvuni_to_utf8((U8 *)new_p, chr ^ 32);
+                   UV chr = utf8_to_uvchr_buf((U8 *)p, (U8 *) end, &len);
+                   new_p = (char *)uvchr_to_utf8((U8 *)new_p, chr ^ 32);
                    p += len;
                }
                SvUTF8_on(newkey);
@@ -532,12 +533,14 @@ STATIC void
 THX_run_cleanup(pTHX_ void *cleanup_code_ref)
 {
     dSP;
+    PUSHSTACK;
     ENTER;
     SAVETMPS;
     PUSHMARK(SP);
     call_sv((SV*)cleanup_code_ref, G_VOID|G_DISCARD);
     FREETMPS;
     LEAVE;
+    POPSTACK;
 }
 
 STATIC OP *
@@ -891,7 +894,9 @@ static OP *THX_parse_keyword_swaplabel(pTHX)
     OP *sop = parse_barestmt(0);
     SV *label = parse_label(PARSE_OPTIONAL);
     if (label) sv_2mortal(label);
-    return newSTATEOP(0, label ? savepv(SvPVX(label)) : NULL, sop);
+    return newSTATEOP(label ? SvUTF8(label) : 0,
+                      label ? savepv(SvPVX(label)) : NULL,
+                      sop);
 }
 
 #define parse_keyword_labelconst() THX_parse_keyword_labelconst(aTHX)
@@ -1047,7 +1052,6 @@ peep_xop(pTHX_ OP *o, OP *oldop)
 static I32
 filter_call(pTHX_ int idx, SV *buf_sv, int maxlen)
 {
-    SV   *my_sv = FILTER_DATA(idx);
     char *p;
     char *end;
     int n = FILTER_READ(idx + 1, buf_sv, maxlen);
@@ -1079,6 +1083,48 @@ XS_EXTERNAL(XS_XS__APItest__XSUB_XS_APIVERSION_invalid);
 
 static struct mro_alg mymro;
 
+static Perl_check_t addissub_nxck_add;
+
+static OP *
+addissub_myck_add(pTHX_ OP *op)
+{
+    SV **flag_svp = hv_fetchs(GvHV(PL_hintgv), "XS::APItest/addissub", 0);
+    OP *aop, *bop;
+    U8 flags;
+    if (!(flag_svp && SvTRUE(*flag_svp) && (op->op_flags & OPf_KIDS) &&
+           (aop = cBINOPx(op)->op_first) && (bop = aop->op_sibling) &&
+           !bop->op_sibling))
+       return addissub_nxck_add(aTHX_ op);
+    aop->op_sibling = NULL;
+    cBINOPx(op)->op_first = NULL;
+    op->op_flags &= ~OPf_KIDS;
+    flags = op->op_flags;
+    op_free(op);
+    return newBINOP(OP_SUBTRACT, flags, aop, bop);
+}
+
+static Perl_check_t old_ck_rv2cv;
+
+static OP *
+my_ck_rv2cv(pTHX_ OP *o)
+{
+    SV *ref;
+    SV **flag_svp = hv_fetchs(GvHV(PL_hintgv), "XS::APItest/addunder", 0);
+    OP *aop;
+
+    if (flag_svp && SvTRUE(*flag_svp) && (o->op_flags & OPf_KIDS)
+     && (aop = cUNOPx(o)->op_first) && aop->op_type == OP_CONST
+     && aop->op_private & (OPpCONST_ENTERED|OPpCONST_BARE)
+     && (ref = cSVOPx(aop)->op_sv) && SvPOK(ref) && SvCUR(ref)
+     && *(SvEND(ref)-1) == 'o')
+    {
+       SvGROW(ref, SvCUR(ref)+2);
+       *SvEND(ref) = '_';
+       SvCUR(ref)++;
+       *SvEND(ref) = '\0';
+    }
+    return old_ck_rv2cv(aTHX_ o);
+}
 
 #include "const-c.inc"
 
@@ -1106,6 +1152,41 @@ bytes_cmp_utf8(bytes, utf8)
     OUTPUT:
        RETVAL
 
+AV *
+test_utf8n_to_uvchr(s, len, flags)
+
+        SV *s
+        SV *len
+        SV *flags
+    PREINIT:
+        STRLEN retlen;
+        UV ret;
+        STRLEN slen;
+
+    CODE:
+        /* Call utf8n_to_uvchr() with the inputs.  It always asks for the
+         * actual length to be returned
+         *
+         * Length to assume <s> is; not checked, so could have buffer overflow
+         */
+        RETVAL = newAV();
+        sv_2mortal((SV*)RETVAL);
+
+        ret
+         = utf8n_to_uvchr((U8*) SvPV(s, slen), SvUV(len), &retlen, SvUV(flags));
+
+        /* Returns the return value in [0]; <retlen> in [1] */
+        av_push(RETVAL, newSVuv(ret));
+        if (retlen == (STRLEN) -1) {
+            av_push(RETVAL, newSViv(-1));
+        }
+        else {
+            av_push(RETVAL, newSVuv(retlen));
+        }
+
+    OUTPUT:
+        RETVAL
+
 MODULE = XS::APItest:Overload  PACKAGE = XS::APItest::Overload
 
 void
@@ -1455,6 +1536,22 @@ refcounted_he_fetch(key, level=0)
 
 #endif
 
+void
+test_force_keys(HV *hv)
+    PREINIT:
+        HE *he;
+       STRLEN count = 0;
+    PPCODE:
+        hv_iterinit(hv);
+        he = hv_iternext(hv);
+        while (he) {
+           SV *sv = HeSVKEY_force(he);
+           ++count;
+           EXTEND(SP, count);
+           PUSHs(sv_mortalcopy(sv));
+            he = hv_iternext(hv);
+        }
+
 =pod
 
 sub TIEHASH  { bless {}, $_[0] }
@@ -1542,6 +1639,7 @@ SV *
 AUTOLOADp(...)
     PROTOTYPE: *$
     CODE:
+        PERL_UNUSED_ARG(items);
        RETVAL = newSVpvn_flags(SvPVX(cv), SvCUR(cv), SvUTF8(cv));
     OUTPUT:
        RETVAL
@@ -1657,6 +1755,28 @@ xop_build_optree ()
     OUTPUT:
         RETVAL
 
+IV
+xop_from_custom_op ()
+    CODE:
+/* author note: this test doesn't imply Perl_custom_op_xop is or isn't public
+   API or that Perl_custom_op_xop is known to be used outside the core */
+        UNOP *unop;
+        XOP *xop;
+
+        NewOp(1102, unop, 1, UNOP);
+        unop->op_type       = OP_CUSTOM;
+        unop->op_ppaddr     = pp_xop;
+        unop->op_flags      = OPf_KIDS;
+        unop->op_private    = 0;
+        unop->op_first      = NULL;
+        unop->op_next       = NULL;
+
+        xop = Perl_custom_op_xop(aTHX_ (OP *)unop);
+        FreeOp(unop);
+        RETVAL = PTR2IV(xop);
+    OUTPUT:
+        RETVAL
+
 BOOT:
 {
     MY_CXT_INIT;
@@ -1825,6 +1945,81 @@ mxpushu()
        mXPUSHu(3);
        XSRETURN(3);
 
+void
+call_sv_C()
+PREINIT:
+    CV * i_sub;
+    GV * i_gv;
+    I32 retcnt;
+    SV * errsv;
+    char * errstr;
+    SV * miscsv = sv_newmortal();
+    HV * hv = (HV*)sv_2mortal((SV*)newHV());
+CODE:
+    i_sub = get_cv("i", 0);
+    PUSHMARK(SP);
+    /* PUTBACK not needed since this sub was called with 0 args, and is calling
+      0 args, so global SP doesn't need to be moved before a call_* */
+    retcnt = call_sv((SV*)i_sub, 0); /* try a CV* */
+    SPAGAIN;
+    SP -= retcnt; /* dont care about return count, wipe everything off */
+    sv_setpvs(miscsv, "i");
+    PUSHMARK(SP);
+    retcnt = call_sv(miscsv, 0); /* try a PV */
+    SPAGAIN;
+    SP -= retcnt;
+    /* no add and SVt_NULL are intentional, sub i should be defined already */
+    i_gv = gv_fetchpvn_flags("i", sizeof("i")-1, 0, SVt_NULL);
+    PUSHMARK(SP);
+    retcnt = call_sv((SV*)i_gv, 0); /* try a GV* */
+    SPAGAIN;
+    SP -= retcnt;
+    /* the tests below are not declaring this being public API behavior,
+       only current internal behavior, these tests can be changed in the
+       future if necessery */
+    PUSHMARK(SP);
+    retcnt = call_sv(&PL_sv_yes, 0); /* does nothing */
+    SPAGAIN;
+    SP -= retcnt;
+    PUSHMARK(SP);
+    retcnt = call_sv(&PL_sv_no, G_EVAL);
+    SPAGAIN;
+    SP -= retcnt;
+    errsv = ERRSV;
+    errstr = SvPV_nolen(errsv);
+    if(strnEQ(errstr, "Undefined subroutine &main:: called at",
+              sizeof("Undefined subroutine &main:: called at") - 1)) {
+        PUSHMARK(SP);
+        retcnt = call_sv((SV*)i_sub, 0); /* call again to increase counter */
+        SPAGAIN;
+        SP -= retcnt;
+    }
+    PUSHMARK(SP);
+    retcnt = call_sv(&PL_sv_undef,  G_EVAL);
+    SPAGAIN;
+    SP -= retcnt;
+    errsv = ERRSV;
+    errstr = SvPV_nolen(errsv);
+    if(strnEQ(errstr, "Can't use an undefined value as a subroutine reference at",
+              sizeof("Can't use an undefined value as a subroutine reference at") - 1)) {
+        PUSHMARK(SP);
+        retcnt = call_sv((SV*)i_sub, 0); /* call again to increase counter */
+        SPAGAIN;
+        SP -= retcnt;
+    }
+    PUSHMARK(SP);
+    retcnt = call_sv((SV*)hv,  G_EVAL);
+    SPAGAIN;
+    SP -= retcnt;
+    errsv = ERRSV;
+    errstr = SvPV_nolen(errsv);
+    if(strnEQ(errstr, "Not a CODE reference at",
+              sizeof("Not a CODE reference at") - 1)) {
+        PUSHMARK(SP);
+        retcnt = call_sv((SV*)i_sub, 0); /* call again to increase counter */
+        SPAGAIN;
+        SP -= retcnt;
+    }
 
 void
 call_sv(sv, flags, ...)
@@ -1878,25 +2073,31 @@ call_method(methname, flags, ...)
        PUSHs(sv_2mortal(newSViv(i)));
 
 void
-newCONSTSUB_type(stash, name, flags, type)
+newCONSTSUB(stash, name, flags, sv)
     HV* stash
     SV* name
     I32 flags
-    int type
+    SV* sv
+    ALIAS:
+       newCONSTSUB_flags = 1
     PREINIT:
-       CV* cv;
+       CV* mycv = NULL;
+       STRLEN len;
+       const char *pv = SvPV(name, len);
     PPCODE:
-        switch (type) {
+        switch (ix) {
            case 0:
-              cv = newCONSTSUB(stash, SvPV_nolen(name), NULL);
+               mycv = newCONSTSUB(stash, pv, SvOK(sv) ? SvREFCNT_inc(sv) : NULL);
                break;
            case 1:
-               cv = newCONSTSUB_flags(stash, SvPV_nolen(name), flags | SvUTF8(name), NULL);
+               mycv = newCONSTSUB_flags(
+                 stash, pv, len, flags | SvUTF8(name), SvOK(sv) ? SvREFCNT_inc(sv) : NULL
+               );
                break;
         }
         EXTEND(SP, 2);
-        PUSHs( CvCONST(cv) ? &PL_sv_yes : &PL_sv_no );
-       PUSHs((SV*)CvGV(cv));
+        PUSHs( CvCONST(mycv) ? &PL_sv_yes : &PL_sv_no );
+        PUSHs((SV*)CvGV(mycv));
 
 void
 gv_init_type(namesv, multi, flags, type)
@@ -1938,7 +2139,7 @@ gv_fetchmeth_type(stash, methname, type, level, flags)
     PREINIT:
         STRLEN len;
         const char * const name = SvPV_const(methname, len);
-       GV* gv;
+       GV* gv = NULL;
     PPCODE:
         switch (type) {
            case 0:
@@ -1966,7 +2167,7 @@ gv_fetchmeth_autoload_type(stash, methname, type, level, flags)
     PREINIT:
         STRLEN len;
         const char * const name = SvPV_const(methname, len);
-       GV* gv;
+       GV* gv = NULL;
     PPCODE:
         switch (type) {
            case 0:
@@ -1991,7 +2192,7 @@ gv_fetchmethod_flags_type(stash, methname, type, flags)
     int type
     I32 flags
     PREINIT:
-       GV* gv;
+       GV* gv = NULL;
     PPCODE:
         switch (type) {
            case 0:
@@ -2021,7 +2222,7 @@ gv_autoload_type(stash, methname, type, method)
     PREINIT:
         STRLEN len;
         const char * const name = SvPV_const(methname, len);
-       GV* gv;
+       GV* gv = NULL;
        I32 flags = method ? GV_AUTOLOAD_ISMETHOD : 0;
     PPCODE:
         switch (type) {
@@ -2047,7 +2248,7 @@ whichsig_type(namesv, type)
     PREINIT:
         STRLEN len;
         const char * const name = SvPV_const(namesv, len);
-        I32 i;
+        I32 i = 0;
     PPCODE:
         switch (type) {
            case 0:
@@ -2422,13 +2623,12 @@ void
 test_rv2cv_op_cv()
     PROTOTYPE:
     PREINIT:
-       GV *troc_gv, *wibble_gv;
+       GV *troc_gv;
        CV *troc_cv;
        OP *o;
     CODE:
        troc_gv = gv_fetchpv("XS::APItest::test_rv2cv_op_cv", 0, SVt_PVGV);
        troc_cv = get_cv("XS::APItest::test_rv2cv_op_cv", 0);
-       wibble_gv = gv_fetchpv("XS::APItest::wibble", 0, SVt_PVGV);
        o = newCVREF(0, newGVOP(OP_GV, 0, troc_gv));
        if (rv2cv_op_cv(o, 0) != troc_cv) croak_fail();
        if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV) != (CV*)troc_gv)
@@ -2652,9 +2852,9 @@ test_coplabel()
         if (len != 3) croak("fail # cop_fetch_label len");
         if (utf8) croak("fail # cop_fetch_label utf8");
         /* SMALL GERMAN UMLAUT A */
-        Perl_cop_store_label(aTHX_ cop, "foä", 4, SVf_UTF8);
+        Perl_cop_store_label(aTHX_ cop, "fo\xc3\xa4", 4, SVf_UTF8);
         label = Perl_cop_fetch_label(aTHX_ cop, &len, &utf8);
-        if (strcmp(label,"foä")) croak("fail # cop_fetch_label label");
+        if (strcmp(label,"fo\xc3\xa4")) croak("fail # cop_fetch_label label");
         if (len != 4) croak("fail # cop_fetch_label len");
         if (!utf8) croak("fail # cop_fetch_label utf8");
 
@@ -2990,6 +3190,7 @@ CODE:
        MULTICALL;
     }
     POP_MULTICALL;
+    PERL_UNUSED_VAR(newsp);
     XSRETURN_UNDEF;
 }
 
@@ -3023,7 +3224,8 @@ CODE:
     PERL_SET_CONTEXT(interp_dup);
 
     /* continue after 'clone_with_stack' */
-    interp_dup->Iop = interp_dup->Iop->op_next;
+    if (interp_dup->Iop)
+       interp_dup->Iop = interp_dup->Iop->op_next;
 
     /* run with new perl */
     Perl_runops_standard(interp_dup);
@@ -3169,7 +3371,14 @@ CODE:
        HeVAL(entry) = NULL;
     }
 
-bool
+HV *
+newHVhv(HV *hv)
+CODE:
+    RETVAL = newHVhv(hv);
+OUTPUT:
+    RETVAL
+
+U32
 SvIsCOW(SV *sv)
 CODE:
     RETVAL = SvIsCOW(sv);
@@ -3194,16 +3403,16 @@ fetch_pad_names( cv )
 CV* cv
  PREINIT:
   I32 i;
-  AV *pad_namelist;
+  PADNAMELIST *pad_namelist;
   AV *retav = newAV();
  CODE:
-  pad_namelist = (AV*) *av_fetch(CvPADLIST(cv), 0, FALSE);
+  pad_namelist = PadlistNAMES(CvPADLIST(cv));
 
-  for ( i = av_len(pad_namelist); i >= 0; i-- ) {
-    SV** name_ptr = av_fetch(pad_namelist, i, 0);
+  for ( i = PadnamelistMAX(pad_namelist); i >= 0; i-- ) {
+    PADNAME* name = PadnamelistARRAY(pad_namelist)[i];
 
-    if (name_ptr && SvPOKp(*name_ptr)) {
-        av_push(retav, newSVsv(*name_ptr));
+    if (PadnameLEN(name)) {
+        av_push(retav, newSVpadname(name));
     }
   }
   RETVAL = newRV_noinc((SV*)retav);
@@ -3226,10 +3435,8 @@ OUTPUT:
 
 void
 stringify(SV *sv)
-PREINIT:
-    const char *pv;
 CODE:
-    pv = SvPV_nolen(sv);
+    (void)SvPV_nolen(sv);
 
 SV *
 HvENAME(HV *hv)
@@ -3243,6 +3450,137 @@ CODE:
 OUTPUT:
     RETVAL
 
+int
+xs_cmp(int a, int b)
+CODE:
+    /* Odd sorting (odd numbers first), to make sure we are actually
+       being called */
+    RETVAL = a % 2 != b % 2
+              ? a % 2 ? -1 : 1
+              : a < b ? -1 : a == b ? 0 : 1;
+OUTPUT:
+    RETVAL
+
+SV *
+xs_cmp_undef(SV *a, SV *b)
+CODE:
+    PERL_UNUSED_ARG(a);
+    PERL_UNUSED_ARG(b);
+    RETVAL = &PL_sv_undef;
+OUTPUT:
+    RETVAL
+
+char *
+SvPVbyte(SV *sv)
+CODE:
+    RETVAL = SvPVbyte_nolen(sv);
+OUTPUT:
+    RETVAL
+
+char *
+SvPVutf8(SV *sv)
+CODE:
+    RETVAL = SvPVutf8_nolen(sv);
+OUTPUT:
+    RETVAL
+
+void
+setup_addissub()
+CODE:
+    wrap_op_checker(OP_ADD, addissub_myck_add, &addissub_nxck_add);
+
+void
+setup_rv2cv_addunderbar()
+CODE:
+    wrap_op_checker(OP_RV2CV, my_ck_rv2cv, &old_ck_rv2cv);
+
+#ifdef USE_ITHREADS
+
+bool
+test_alloccopstash()
+CODE:
+    RETVAL = PL_stashpad[alloccopstash(PL_defstash)] == PL_defstash;
+OUTPUT:
+    RETVAL
+
+#endif
+
+bool
+test_newFOROP_without_slab()
+CODE:
+    {
+       const I32 floor = start_subparse(0,0);
+       /* The slab allocator does not like CvROOT being set. */
+       CvROOT(PL_compcv) = (OP *)1;
+       op_free(newFOROP(0, 0, newOP(OP_PUSHMARK, 0), 0, 0));
+       CvROOT(PL_compcv) = NULL;
+       SvREFCNT_dec(PL_compcv);
+       LEAVE_SCOPE(floor);
+       /* If we have not crashed yet, then the test passes. */
+       RETVAL = TRUE;
+    }
+OUTPUT:
+    RETVAL
+
+ # provide access to CALLREGEXEC, except replace pointers within the
+ # string with offsets from the start of the string
+
+I32
+callregexec(SV *prog, STRLEN stringarg, STRLEN strend, I32 minend, SV *sv, U32 nosave)
+CODE:
+    {
+       STRLEN len;
+       char *strbeg;
+       if (SvROK(prog))
+           prog = SvRV(prog);
+       strbeg = SvPV_force(sv, len);
+       RETVAL = CALLREGEXEC((REGEXP *)prog,
+                           strbeg + stringarg,
+                           strbeg + strend,
+                           strbeg,
+                           minend,
+                           sv,
+                           NULL, /* data */
+                           nosave);
+    }
+OUTPUT:
+    RETVAL
+
+void
+lexical_import(SV *name, CV *cv)
+    CODE:
+    {
+       PADLIST *pl;
+       PADOFFSET off;
+       if (!PL_compcv)
+           Perl_croak(aTHX_
+                     "lexical_import can only be called at compile time");
+       pl = CvPADLIST(PL_compcv);
+       ENTER;
+       SAVESPTR(PL_comppad_name); PL_comppad_name = PadlistNAMES(pl);
+       SAVESPTR(PL_comppad);      PL_comppad      = PadlistARRAY(pl)[1];
+       SAVESPTR(PL_curpad);       PL_curpad       = PadARRAY(PL_comppad);
+       off = pad_add_name_sv(sv_2mortal(newSVpvf("&%"SVf,name)),
+                             padadd_STATE, 0, 0);
+       SvREFCNT_dec(PL_curpad[off]);
+       PL_curpad[off] = SvREFCNT_inc(cv);
+       LEAVE;
+    }
+
+SV *
+sv_mortalcopy(SV *sv)
+    CODE:
+       RETVAL = SvREFCNT_inc(sv_mortalcopy(sv));
+    OUTPUT:
+       RETVAL
+
+SV *
+newRV(SV *sv)
+
+void
+alias_av(AV *av, IV ix, SV *sv)
+    CODE:
+       av_store(av, ix, SvREFCNT_inc(sv));
 
 MODULE = XS::APItest PACKAGE = XS::APItest::AUTOLOADtest
 
@@ -3251,8 +3589,8 @@ AUTOLOAD(...)
   INIT:
     SV* comms;
     SV* class_and_method;
-    SV* tmp;
   CODE:
+    PERL_UNUSED_ARG(items);
     class_and_method = GvSV(CvGV(cv));
     comms = get_sv("main::the_method", 1);
     if (class_and_method == NULL) {
@@ -3334,11 +3672,1122 @@ test_get_vtbl()
 #ifdef USE_LOCALE_COLLATE
        test_get_this_vtable(collxfrm);
 #endif
-       test_get_this_vtable(amagic);
-       test_get_this_vtable(amagicelem);
        test_get_this_vtable(backref);
        test_get_this_vtable(utf8);
 
        RETVAL = PTR2UV(get_vtbl(-1));
     OUTPUT:
        RETVAL
+
+bool
+test_isBLANK_uni(UV ord)
+    CODE:
+        RETVAL = isBLANK_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isBLANK_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isBLANK_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isBLANK_A(UV ord)
+    CODE:
+        RETVAL = isBLANK_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isBLANK_L1(UV ord)
+    CODE:
+        RETVAL = isBLANK_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isBLANK_LC(UV ord)
+    CODE:
+        RETVAL = isBLANK_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isBLANK_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isBLANK_utf8(p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isBLANK_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isBLANK_LC_utf8(p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isVERTWS_uni(UV ord)
+    CODE:
+        RETVAL = isVERTWS_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isVERTWS_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isVERTWS_utf8(p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isUPPER_uni(UV ord)
+    CODE:
+        RETVAL = isUPPER_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isUPPER_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isUPPER_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isUPPER_A(UV ord)
+    CODE:
+        RETVAL = isUPPER_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isUPPER_L1(UV ord)
+    CODE:
+        RETVAL = isUPPER_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isUPPER_LC(UV ord)
+    CODE:
+        RETVAL = isUPPER_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isUPPER_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isUPPER_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isUPPER_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isUPPER_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isLOWER_uni(UV ord)
+    CODE:
+        RETVAL = isLOWER_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isLOWER_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isLOWER_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isLOWER_A(UV ord)
+    CODE:
+        RETVAL = isLOWER_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isLOWER_L1(UV ord)
+    CODE:
+        RETVAL = isLOWER_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isLOWER_LC(UV ord)
+    CODE:
+        RETVAL = isLOWER_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isLOWER_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isLOWER_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isLOWER_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isLOWER_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHA_uni(UV ord)
+    CODE:
+        RETVAL = isALPHA_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHA_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isALPHA_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHA_A(UV ord)
+    CODE:
+        RETVAL = isALPHA_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHA_L1(UV ord)
+    CODE:
+        RETVAL = isALPHA_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHA_LC(UV ord)
+    CODE:
+        RETVAL = isALPHA_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHA_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isALPHA_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHA_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isALPHA_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isWORDCHAR_uni(UV ord)
+    CODE:
+        RETVAL = isWORDCHAR_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isWORDCHAR_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isWORDCHAR_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isWORDCHAR_A(UV ord)
+    CODE:
+        RETVAL = isWORDCHAR_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isWORDCHAR_L1(UV ord)
+    CODE:
+        RETVAL = isWORDCHAR_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isWORDCHAR_LC(UV ord)
+    CODE:
+        RETVAL = isWORDCHAR_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isWORDCHAR_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isWORDCHAR_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isWORDCHAR_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isWORDCHAR_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHANUMERIC_uni(UV ord)
+    CODE:
+        RETVAL = isALPHANUMERIC_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHANUMERIC_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isALPHANUMERIC_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHANUMERIC_A(UV ord)
+    CODE:
+        RETVAL = isALPHANUMERIC_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHANUMERIC_L1(UV ord)
+    CODE:
+        RETVAL = isALPHANUMERIC_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHANUMERIC_LC(UV ord)
+    CODE:
+        RETVAL = isALPHANUMERIC_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHANUMERIC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isALPHANUMERIC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALPHANUMERIC_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isALPHANUMERIC_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALNUM_uni(UV ord)
+    CODE:
+        RETVAL = isALNUM_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALNUM_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isALNUM_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALNUM_LC(UV ord)
+    CODE:
+        RETVAL = isALNUM_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALNUM_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isALNUM_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isALNUM_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isALNUM_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isDIGIT_uni(UV ord)
+    CODE:
+        RETVAL = isDIGIT_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isDIGIT_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isDIGIT_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isDIGIT_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isDIGIT_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isDIGIT_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isDIGIT_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isDIGIT_A(UV ord)
+    CODE:
+        RETVAL = isDIGIT_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isDIGIT_L1(UV ord)
+    CODE:
+        RETVAL = isDIGIT_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isDIGIT_LC(UV ord)
+    CODE:
+        RETVAL = isDIGIT_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDFIRST_uni(UV ord)
+    CODE:
+        RETVAL = isIDFIRST_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDFIRST_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isIDFIRST_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDFIRST_A(UV ord)
+    CODE:
+        RETVAL = isIDFIRST_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDFIRST_L1(UV ord)
+    CODE:
+        RETVAL = isIDFIRST_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDFIRST_LC(UV ord)
+    CODE:
+        RETVAL = isIDFIRST_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDFIRST_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isIDFIRST_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDFIRST_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isIDFIRST_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDCONT_uni(UV ord)
+    CODE:
+        RETVAL = isIDCONT_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDCONT_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isIDCONT_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDCONT_A(UV ord)
+    CODE:
+        RETVAL = isIDCONT_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDCONT_L1(UV ord)
+    CODE:
+        RETVAL = isIDCONT_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDCONT_LC(UV ord)
+    CODE:
+        RETVAL = isIDCONT_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDCONT_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isIDCONT_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isIDCONT_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isIDCONT_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isSPACE_uni(UV ord)
+    CODE:
+        RETVAL = isSPACE_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isSPACE_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isSPACE_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isSPACE_A(UV ord)
+    CODE:
+        RETVAL = isSPACE_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isSPACE_L1(UV ord)
+    CODE:
+        RETVAL = isSPACE_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isSPACE_LC(UV ord)
+    CODE:
+        RETVAL = isSPACE_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isSPACE_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isSPACE_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isSPACE_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isSPACE_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isASCII_uni(UV ord)
+    CODE:
+        RETVAL = isASCII_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isASCII_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isASCII_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isASCII_A(UV ord)
+    CODE:
+        RETVAL = isASCII_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isASCII_L1(UV ord)
+    CODE:
+        RETVAL = isASCII_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isASCII_LC(UV ord)
+    CODE:
+        RETVAL = isASCII_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isASCII_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isASCII_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isASCII_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isASCII_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isCNTRL_uni(UV ord)
+    CODE:
+        RETVAL = isCNTRL_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isCNTRL_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isCNTRL_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isCNTRL_A(UV ord)
+    CODE:
+        RETVAL = isCNTRL_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isCNTRL_L1(UV ord)
+    CODE:
+        RETVAL = isCNTRL_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isCNTRL_LC(UV ord)
+    CODE:
+        RETVAL = isCNTRL_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isCNTRL_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isCNTRL_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isCNTRL_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isCNTRL_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPRINT_uni(UV ord)
+    CODE:
+        RETVAL = isPRINT_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPRINT_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isPRINT_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPRINT_A(UV ord)
+    CODE:
+        RETVAL = isPRINT_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPRINT_L1(UV ord)
+    CODE:
+        RETVAL = isPRINT_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPRINT_LC(UV ord)
+    CODE:
+        RETVAL = isPRINT_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPRINT_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isPRINT_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPRINT_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isPRINT_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isGRAPH_uni(UV ord)
+    CODE:
+        RETVAL = isGRAPH_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isGRAPH_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isGRAPH_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isGRAPH_A(UV ord)
+    CODE:
+        RETVAL = isGRAPH_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isGRAPH_L1(UV ord)
+    CODE:
+        RETVAL = isGRAPH_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isGRAPH_LC(UV ord)
+    CODE:
+        RETVAL = isGRAPH_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isGRAPH_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isGRAPH_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isGRAPH_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isGRAPH_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPUNCT_uni(UV ord)
+    CODE:
+        RETVAL = isPUNCT_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPUNCT_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isPUNCT_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPUNCT_A(UV ord)
+    CODE:
+        RETVAL = isPUNCT_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPUNCT_L1(UV ord)
+    CODE:
+        RETVAL = isPUNCT_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPUNCT_LC(UV ord)
+    CODE:
+        RETVAL = isPUNCT_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPUNCT_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isPUNCT_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPUNCT_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isPUNCT_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isXDIGIT_uni(UV ord)
+    CODE:
+        RETVAL = isXDIGIT_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isXDIGIT_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isXDIGIT_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isXDIGIT_A(UV ord)
+    CODE:
+        RETVAL = isXDIGIT_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isXDIGIT_L1(UV ord)
+    CODE:
+        RETVAL = isXDIGIT_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isXDIGIT_LC(UV ord)
+    CODE:
+        RETVAL = isXDIGIT_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isXDIGIT_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isXDIGIT_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isXDIGIT_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isXDIGIT_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPSXSPC_uni(UV ord)
+    CODE:
+        RETVAL = isPSXSPC_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPSXSPC_LC_uvchr(UV ord)
+    CODE:
+        RETVAL = isPSXSPC_LC_uvchr(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPSXSPC_A(UV ord)
+    CODE:
+        RETVAL = isPSXSPC_A(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPSXSPC_L1(UV ord)
+    CODE:
+        RETVAL = isPSXSPC_L1(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPSXSPC_LC(UV ord)
+    CODE:
+        RETVAL = isPSXSPC_LC(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPSXSPC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isPSXSPC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isPSXSPC_LC_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isPSXSPC_LC_utf8( p);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isQUOTEMETA(UV ord)
+    CODE:
+        RETVAL = _isQUOTEMETA(ord);
+    OUTPUT:
+        RETVAL
+
+UV
+test_toLOWER(UV ord)
+    CODE:
+        RETVAL = toLOWER(ord);
+    OUTPUT:
+        RETVAL
+
+UV
+test_toLOWER_L1(UV ord)
+    CODE:
+        RETVAL = toLOWER_L1(ord);
+    OUTPUT:
+        RETVAL
+
+UV
+test_toLOWER_LC(UV ord)
+    CODE:
+        RETVAL = toLOWER_LC(ord);
+    OUTPUT:
+        RETVAL
+
+AV *
+test_toLOWER_uni(UV ord)
+    PREINIT:
+        U8 s[UTF8_MAXBYTES_CASE + 1];
+        STRLEN len;
+        AV *av;
+        SV *utf8;
+    CODE:
+        av = newAV();
+        av_push(av, newSVuv(toLOWER_uni(ord, s, &len)));
+
+        utf8 = newSVpvn((char *) s, len);
+        SvUTF8_on(utf8);
+        av_push(av, utf8);
+
+        av_push(av, newSVuv(len));
+        RETVAL = av;
+    OUTPUT:
+        RETVAL
+
+AV *
+test_toLOWER_utf8(SV * p)
+    PREINIT:
+        U8 *input;
+        U8 s[UTF8_MAXBYTES_CASE + 1];
+        STRLEN len;
+        AV *av;
+        SV *utf8;
+    CODE:
+        input = (U8 *) SvPV(p, len);
+        av = newAV();
+        av_push(av, newSVuv(toLOWER_utf8(input, s, &len)));
+
+        utf8 = newSVpvn((char *) s, len);
+        SvUTF8_on(utf8);
+        av_push(av, utf8);
+
+        av_push(av, newSVuv(len));
+        RETVAL = av;
+    OUTPUT:
+        RETVAL
+
+UV
+test_toFOLD(UV ord)
+    CODE:
+        RETVAL = toFOLD(ord);
+    OUTPUT:
+        RETVAL
+
+UV
+test_toFOLD_LC(UV ord)
+    CODE:
+        RETVAL = toFOLD_LC(ord);
+    OUTPUT:
+        RETVAL
+
+AV *
+test_toFOLD_uni(UV ord)
+    PREINIT:
+        U8 s[UTF8_MAXBYTES_CASE + 1];
+        STRLEN len;
+        AV *av;
+        SV *utf8;
+    CODE:
+        av = newAV();
+        av_push(av, newSVuv(toFOLD_uni(ord, s, &len)));
+
+        utf8 = newSVpvn((char *) s, len);
+        SvUTF8_on(utf8);
+        av_push(av, utf8);
+
+        av_push(av, newSVuv(len));
+        RETVAL = av;
+    OUTPUT:
+        RETVAL
+
+AV *
+test_toFOLD_utf8(SV * p)
+    PREINIT:
+        U8 *input;
+        U8 s[UTF8_MAXBYTES_CASE + 1];
+        STRLEN len;
+        AV *av;
+        SV *utf8;
+    CODE:
+        input = (U8 *) SvPV(p, len);
+        av = newAV();
+        av_push(av, newSVuv(toFOLD_utf8(input, s, &len)));
+
+        utf8 = newSVpvn((char *) s, len);
+        SvUTF8_on(utf8);
+        av_push(av, utf8);
+
+        av_push(av, newSVuv(len));
+        RETVAL = av;
+    OUTPUT:
+        RETVAL
+
+UV
+test_toUPPER(UV ord)
+    CODE:
+        RETVAL = toUPPER(ord);
+    OUTPUT:
+        RETVAL
+
+UV
+test_toUPPER_LC(UV ord)
+    CODE:
+        RETVAL = toUPPER_LC(ord);
+    OUTPUT:
+        RETVAL
+
+AV *
+test_toUPPER_uni(UV ord)
+    PREINIT:
+        U8 s[UTF8_MAXBYTES_CASE + 1];
+        STRLEN len;
+        AV *av;
+        SV *utf8;
+    CODE:
+        av = newAV();
+        av_push(av, newSVuv(toUPPER_uni(ord, s, &len)));
+
+        utf8 = newSVpvn((char *) s, len);
+        SvUTF8_on(utf8);
+        av_push(av, utf8);
+
+        av_push(av, newSVuv(len));
+        RETVAL = av;
+    OUTPUT:
+        RETVAL
+
+AV *
+test_toUPPER_utf8(SV * p)
+    PREINIT:
+        U8 *input;
+        U8 s[UTF8_MAXBYTES_CASE + 1];
+        STRLEN len;
+        AV *av;
+        SV *utf8;
+    CODE:
+        input = (U8 *) SvPV(p, len);
+        av = newAV();
+        av_push(av, newSVuv(toUPPER_utf8(input, s, &len)));
+
+        utf8 = newSVpvn((char *) s, len);
+        SvUTF8_on(utf8);
+        av_push(av, utf8);
+
+        av_push(av, newSVuv(len));
+        RETVAL = av;
+    OUTPUT:
+        RETVAL
+
+UV
+test_toTITLE(UV ord)
+    CODE:
+        RETVAL = toTITLE(ord);
+    OUTPUT:
+        RETVAL
+
+AV *
+test_toTITLE_uni(UV ord)
+    PREINIT:
+        U8 s[UTF8_MAXBYTES_CASE + 1];
+        STRLEN len;
+        AV *av;
+        SV *utf8;
+    CODE:
+        av = newAV();
+        av_push(av, newSVuv(toTITLE_uni(ord, s, &len)));
+
+        utf8 = newSVpvn((char *) s, len);
+        SvUTF8_on(utf8);
+        av_push(av, utf8);
+
+        av_push(av, newSVuv(len));
+        RETVAL = av;
+    OUTPUT:
+        RETVAL
+
+AV *
+test_toTITLE_utf8(SV * p)
+    PREINIT:
+        U8 *input;
+        U8 s[UTF8_MAXBYTES_CASE + 1];
+        STRLEN len;
+        AV *av;
+        SV *utf8;
+    CODE:
+        input = (U8 *) SvPV(p, len);
+        av = newAV();
+        av_push(av, newSVuv(toTITLE_utf8(input, s, &len)));
+
+        utf8 = newSVpvn((char *) s, len);
+        SvUTF8_on(utf8);
+        av_push(av, utf8);
+
+        av_push(av, newSVuv(len));
+        RETVAL = av;
+    OUTPUT:
+        RETVAL