text: /* $Id: string.c 3553 2011-05-05 06:14:19Z nanang $ */
  /* 
   * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
   * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
   */
  #include <pj/string.h>
  #include <pj/pool.h>
  #include <pj/log.h>
  #include <pj/os.h>
  #include "test.h"
  
  #define THIS_FILE	"string.c"
  
  /**
   * \page page_pjlib_string_test Test: String
   *
   * This file provides implementation of \b string_test(). It tests the
   * functionality of the string API.
   *
   * \section sleep_test_sec Scope of the Test
   *
   * API tested:
   *  - pj_str()
   *  - pj_strcmp()
   *  - pj_strcmp2()
   *  - pj_stricmp()
   *  - pj_strlen()
   *  - pj_strncmp()
   *  - pj_strnicmp()
   *  - pj_strchr()
   *  - pj_strdup()
   *  - pj_strdup2()
   *  - pj_strcpy()
   *  - pj_strcat()
   *  - pj_strtrim()
   *  - pj_utoa()
   *  - pj_strtoul()
   *  - pj_strtoul2()
   *  - pj_create_random_string()
   *  - ... and mode..
   *
   * This file is <b>pjlib-test/string.c</b>
   *
   * \include pjlib-test/string.c
   */
  
  #if INCLUDE_STRING_TEST
  
  #ifdef _MSC_VER
  #   pragma warning(disable: 4204)
  #endif
  
  #define HELLO_WORLD	"Hello World"
  #define HELLO_WORLD_LEN	11
  #define JUST_HELLO	"Hello"
  #define JUST_HELLO_LEN	5
  #define UL_VALUE	3456789012UL
  
  #if 1
  /* See if both integers have the same sign */
  PJ_INLINE(int) cmp(const char *expr, int i, int j)
  {
      int r = !((i>0 && j>0) || (i<0 && j<0) || (i==0 && j==0));
      if (r) {
  	PJ_LOG(3,(THIS_FILE,"   error: %s: expecting %d, got %d", expr, j, i));
      }
      return r;
  }
  #else
  /* For strict comparison, must be equal */
  PJ_INLINE(int) cmp(const char *expr, int i, int j)
  {
      PJ_UNUSED_ARG(expr);
      return i!=j;
  }
  #endif
  
  #define C(expr, res)	cmp(#expr, expr, res)
  
  static int stricmp_test(void)
  {
  /* This specificly tests and benchmark pj_stricmp(), pj_stricmp_alnum().
   * In addition, it also tests pj_stricmp2(), pj_strnicmp(), and 
   * pj_strnicmp2().
   */
  #define STRTEST(res,res2,S1,S2,code)	\
  	    do { \
  		s1.ptr=S1; s1.slen=(S1)?len:0; \
  		s2.ptr=S2; s2.slen=(S2)?len:0; \
  		pj_get_timestamp(&t1); \
  	        if (C(pj_stricmp(&s1,&s2),res)) return code; \
  		pj_get_timestamp(&t2); \
  		pj_sub_timestamp(&t2, &t1); \
  		pj_add_timestamp(&e1, &t2); \
  		pj_get_timestamp(&t1); \
  	        if (C(pj_stricmp_alnum(&s1,&s2),res)) return code-1; \
  		pj_get_timestamp(&t2); \
  		pj_sub_timestamp(&t2, &t1); \
  		pj_add_timestamp(&e2, &t2); \
  		if (C(pj_stricmp2(&s1,S2),res2)) return code*10; \
  		if (C(pj_strnicmp(&s1,&s2,len),res)) return code*100; \
  		if (C(pj_strnicmp2(&s1,S2,len),res)) return code*1000; \
  	    } while (0)
  
      char *buf;
      pj_str_t s1, s2;
      pj_timestamp t1, t2, e1, e2, zero;
      pj_uint32_t c1, c2;
      int len;
  
      e1.u32.hi = e1.u32.lo = e2.u32.hi = e2.u32.lo = 0;
  
      pj_thread_sleep(0);
  
  #define SNULL 0
  
      /* Compare empty strings. */
      len=0;
      STRTEST( 0, 0, "","",-500);
      STRTEST( 0, 0, SNULL,"",-502);
      STRTEST( 0, 0, "",SNULL,-504);
      STRTEST( 0, 0, SNULL,SNULL,-506);
      STRTEST( 0, -1, "hello","world",-508);
  
      /* equal, length=1 
       * use buffer to simulate non-aligned string.
       */
      buf = "a""A";
      len=1;
      STRTEST( 0,  -1, "a",buf+0,-510);
      STRTEST( 0,  0, "a",buf+1,-512);
      STRTEST(-1, -1, "O", "P", -514);
      STRTEST(-1, -1, SNULL, "a", -516);
      STRTEST( 1,  1, "a", SNULL, -518);
  
      /* equal, length=2 
       * use buffer to simulate non-aligned string.
       */
      buf = "aa""Aa""aA""AA";
      len=2;
      STRTEST( 0, -1, "aa",buf+0,-520);
      STRTEST( 0, -1, "aa",buf+2,-522);
      STRTEST( 0, -1, "aa",buf+4,-524);
      STRTEST( 0, 0, "aa",buf+6,-524);
  
      /* equal, length=3 
       * use buffer to simulate non-aligned string.
       */
      buf = "aaa""Aaa""aAa""aaA""AAa""aAA""AaA""AAA";
      len=3;
      STRTEST( 0, -1, "aaa",buf+0,-530);
      STRTEST( 0, -1, "aaa",buf+3,-532);
      STRTEST( 0, -1, "aaa",buf+6,-534);
      STRTEST( 0, -1, "aaa",buf+9,-536);
      STRTEST( 0, -1, "aaa",buf+12,-538);
      STRTEST( 0, -1, "aaa",buf+15,-540);
      STRTEST( 0, -1, "aaa",buf+18,-542);
      STRTEST( 0, 0, "aaa",buf+21,-534);
  
      /* equal, length=4 */
      len=4;
      STRTEST( 0, 0, "aaaa","aaaa",-540);
      STRTEST( 0, 0, "aaaa","Aaaa",-542);
      STRTEST( 0, 0, "aaaa","aAaa",-544);
      STRTEST( 0, 0, "aaaa","aaAa",-546);
      STRTEST( 0, 0, "aaaa","aaaA",-548);
      STRTEST( 0, 0, "aaaa","AAaa",-550);
      STRTEST( 0, 0, "aaaa","aAAa",-552);
      STRTEST( 0, 0, "aaaa","aaAA",-554);
      STRTEST( 0, 0, "aaaa","AaAa",-556);
      STRTEST( 0, 0, "aaaa","aAaA",-558);
      STRTEST( 0, 0, "aaaa","AaaA",-560);
      STRTEST( 0, 0, "aaaa","AAAa",-562);
      STRTEST( 0, 0, "aaaa","aAAA",-564);
      STRTEST( 0, 0, "aaaa","AAaA",-566);
      STRTEST( 0, 0, "aaaa","AaAA",-568);
      STRTEST( 0, 0, "aaaa","AAAA",-570);
  
      /* equal, length=5 */
      buf = "aaaAa""AaaaA""AaAaA""AAAAA";
      len=5;
      STRTEST( 0, -1, "aaaaa",buf+0,-580);
      STRTEST( 0, -1, "aaaaa",buf+5,-582);
      STRTEST( 0, -1, "aaaaa",buf+10,-584);
      STRTEST( 0, 0, "aaaaa",buf+15,-586);
  
      /* not equal, length=1 */
      len=1;
      STRTEST( -1, -1, "a", "b", -600);
  
      /* not equal, length=2 */
      buf = "ab""ba";
      len=2;
      STRTEST( -1, -1, "aa", buf+0, -610);
      STRTEST( -1, -1, "aa", buf+2, -612);
  
      /* not equal, length=3 */
      buf = "aab""aba""baa";
      len=3;
      STRTEST( -1, -1, "aaa", buf+0, -620);
      STRTEST( -1, -1, "aaa", buf+3, -622);
      STRTEST( -1, -1, "aaa", buf+6, -624);
  
      /* not equal, length=4 */
      buf = "aaab""aaba""abaa""baaa";
      len=4;
      STRTEST( -1, -1, "aaaa", buf+0, -630);
      STRTEST( -1, -1, "aaaa", buf+4, -632);
      STRTEST( -1, -1, "aaaa", buf+8, -634);
      STRTEST( -1, -1, "aaaa", buf+12, -636);
  
      /* not equal, length=5 */
      buf="aaaab""aaaba""aabaa""abaaa""baaaa";
      len=5;
      STRTEST( -1, -1, "aaaaa", buf+0, -640);
      STRTEST( -1, -1, "aaaaa", buf+5, -642);
      STRTEST( -1, -1, "aaaaa", buf+10, -644);
      STRTEST( -1, -1, "aaaaa", buf+15, -646);
      STRTEST( -1, -1, "aaaaa", buf+20, -648);
  
      zero.u32.hi = zero.u32.lo = 0;
      c1 = pj_elapsed_cycle(&zero, &e1);
      c2 = pj_elapsed_cycle(&zero, &e2);
  
      if (c1 < c2) {
  	PJ_LOG(3,("", "  info: pj_stricmp_alnum is slower than pj_stricmp!"));
  	//return -700;
      }
  
      /* Avoid division by zero */
      if (c2 == 0) c2=1;
      
      PJ_LOG(3, ("", "  time: stricmp=%u, stricmp_alnum=%u (speedup=%d.%02dx)", 
  		   c1, c2,
  		   (c1 * 100 / c2) / 100,
  		   (c1 * 100 / c2) % 100));
      return 0;
  #undef STRTEST
  }
  
  /* This tests pj_strcmp(), pj_strcmp2(), pj_strncmp(), pj_strncmp2() */
  static int strcmp_test(void)
  {
  #define STR_TEST(res,S1,S2,code)    \
  	    do { \
  		s1.ptr=S1; s1.slen=S1?len:0; \
  		s2.ptr=S2; s2.slen=S2?len:0; \
  	        if (C(pj_strcmp(&s1,&s2),res)) return code; \
  		if (C(pj_strcmp2(&s1,S2),res)) return code-1; \
  		if (C(pj_strncmp(&s1,&s2,len),res)) return code-2; \
  		if (C(pj_strncmp2(&s1,S2,len),res)) return code-3; \
  	    } while (0)
  
      pj_str_t s1, s2;
      int len;
      
      /* Test with length == 0 */
      len=0;
      STR_TEST(0, "", "", -400);
      STR_TEST(0, SNULL, "", -405);
      STR_TEST(0, "", SNULL, -410);
      STR_TEST(0, SNULL, SNULL, -415);
      STR_TEST(0, "hello", "", -420);
      STR_TEST(0, "hello", SNULL, -425);
  
      /* Test with length != 0 */
      len = 2;
      STR_TEST(0, "12", "12", -430);
      STR_TEST(1, "12", "1", -435);
      STR_TEST(-1, "1", "12", -440);
      STR_TEST(-1, SNULL, "12", -445);
      STR_TEST(1, "12", SNULL, -450);
  
      return 0;
  
  #undef STR_TEST
  }
  
  int string_test(void)
  {
      const pj_str_t hello_world = { HELLO_WORLD, HELLO_WORLD_LEN };
      const pj_str_t just_hello = { JUST_HELLO, JUST_HELLO_LEN };
      pj_str_t s1, s2, s3, s4, s5;
      enum { RCOUNT = 10, RLEN = 16 };
      pj_str_t random[RCOUNT];
      pj_pool_t *pool;
      int i;
  
      pool = pj_pool_create(mem, SNULL, 4096, 0, SNULL);
      if (!pool) return -5;
      
      /* 
       * pj_str(), pj_strcmp(), pj_stricmp(), pj_strlen(), 
       * pj_strncmp(), pj_strchr() 
       */
      s1 = pj_str(HELLO_WORLD);
      if (pj_strcmp(&s1, &hello_world) != 0)
  	return -10;
      if (pj_stricmp(&s1, &hello_world) != 0)
  	return -20;
      if (pj_strcmp(&s1, &just_hello) <= 0)
  	return -30;
      if (pj_stricmp(&s1, &just_hello) <= 0)
  	return -40;
      if (pj_strlen(&s1) != strlen(HELLO_WORLD))
  	return -50;
      if (pj_strncmp(&s1, &hello_world, 5) != 0)
  	return -60;
      if (pj_strnicmp(&s1, &hello_world, 5) != 0)
  	return -70;
      if (pj_strchr(&s1, HELLO_WORLD[1]) != s1.ptr+1)
  	return -80;
  
      /* 
       * pj_strdup() 
       */
      if (!pj_strdup(pool, &s2, &s1))
  	return -100;
      if (pj_strcmp(&s1, &s2) != 0)
  	return -110;
      
      /* 
       * pj_strcpy(), pj_strcat() 
       */
      s3.ptr = (char*) pj_pool_alloc(pool, 256);
      if (!s3.ptr) 
  	return -200;
      pj_strcpy(&s3, &s2);
      pj_strcat(&s3, &just_hello);
  
      if (pj_strcmp2(&s3, HELLO_WORLD JUST_HELLO) != 0)
  	return -210;
  
      /* 
       * pj_strdup2(), pj_strtrim(). 
       */
      pj_strdup2(pool, &s4, " " HELLO_WORLD "\t ");
      pj_strtrim(&s4);
      if (pj_strcmp2(&s4, HELLO_WORLD) != 0)
  	return -250;
  
      /* 
       * pj_utoa() 
       */
      s5.ptr = (char*) pj_pool_alloc(pool, 16);
      if (!s5.ptr)
  	return -270;
      s5.slen = pj_utoa(UL_VALUE, s5.ptr);
  
      /* 
       * pj_strtoul() 
       */
      if (pj_strtoul(&s5) != UL_VALUE)
  	return -280;
  
      /*
       * pj_strtoul2()
       */
      s5 = pj_str("123456");
  
      pj_strtoul2(&s5, SNULL, 10);	/* Crash test */
  
      if (pj_strtoul2(&s5, &s4, 10) != 123456UL)
  	return -290;
      if (s4.slen != 0)
  	return -291;
      if (pj_strtoul2(&s5, &s4, 16) != 0x123456UL)
  	return -292;
  
      s5 = pj_str("0123ABCD");
      if (pj_strtoul2(&s5, &s4, 10) != 123)
  	return -293;
      if (s4.slen != 4)
  	return -294;
      if (s4.ptr == SNULL || *s4.ptr != 'A')
  	return -295;
      if (pj_strtoul2(&s5, &s4, 16) != 0x123ABCDUL)
  	return -296;
      if (s4.slen != 0)
  	return -297;
  
      /* 
       * pj_create_random_string() 
       * Check that no duplicate strings are returned.
       */
      for (i=0; i<RCOUNT; ++i) {
  	int j;
  	
  	random[i].ptr = (char*) pj_pool_alloc(pool, RLEN);
  	if (!random[i].ptr)
  	    return -320;
  
          random[i].slen = RLEN;
  	pj_create_random_string(random[i].ptr, RLEN);
  
  	for (j=0; j<i; ++j) {
  	    if (pj_strcmp(&random[i], &random[j])==0)
  		return -330;
  	}
      }
  
      /* Done. */
      pj_pool_release(pool);
  
      /* Case sensitive comparison test. */
      i = strcmp_test();
      if (i != 0)
  	return i;
  
      /* Caseless comparison test. */
      i = stricmp_test();
      if (i != 0)
  	return i;
  
      return 0;
  }
  
  #else
  /* To prevent warning about "translation unit is empty"
   * when this test is disabled. 
   */
  int dummy_string_test;
  #endif	/* INCLUDE_STRING_TEST */
  
  