diff options
Diffstat (limited to 'unittest')
-rw-r--r-- | unittest/Makefile.am | 5 | ||||
-rw-r--r-- | unittest/examples/Makefile.am | 3 | ||||
-rw-r--r-- | unittest/mysys/CMakeLists.txt | 31 | ||||
-rw-r--r-- | unittest/mysys/Makefile.am | 13 | ||||
-rw-r--r-- | unittest/mysys/base64-t.c | 1 | ||||
-rw-r--r-- | unittest/mysys/bitmap-t.c | 184 | ||||
-rw-r--r-- | unittest/mysys/lf-t.c | 178 | ||||
-rw-r--r-- | unittest/mysys/ma_dyncol-t.c | 795 | ||||
-rw-r--r-- | unittest/mysys/my_atomic-t.c | 146 | ||||
-rw-r--r-- | unittest/mysys/my_vsnprintf-t.c | 155 | ||||
-rw-r--r-- | unittest/mysys/thr_template.c | 94 | ||||
-rw-r--r-- | unittest/mysys/waiting_threads-t.c | 291 | ||||
-rw-r--r-- | unittest/mytap/CMakeLists.txt | 18 | ||||
-rw-r--r-- | unittest/mytap/Makefile.am | 5 | ||||
-rw-r--r-- | unittest/mytap/t/Makefile.am | 3 | ||||
-rw-r--r-- | unittest/mytap/tap.c | 125 | ||||
-rw-r--r-- | unittest/mytap/tap.h | 47 | ||||
-rw-r--r-- | unittest/strings/Makefile.am | 3 | ||||
-rw-r--r-- | unittest/strings/strings-t.c | 2 | ||||
-rw-r--r-- | unittest/unit.pl | 23 |
20 files changed, 2066 insertions, 56 deletions
diff --git a/unittest/Makefile.am b/unittest/Makefile.am index 889e029f6ef..01d27eb8cd2 100644 --- a/unittest/Makefile.am +++ b/unittest/Makefile.am @@ -18,13 +18,10 @@ SUBDIRS = mytap . mysys examples strings EXTRA_DIST = unit.pl CLEANFILES = unit -unittests = mytap mysys strings @mysql_se_unittest_dirs@ @mysql_pg_unittest_dirs@ +unittests = mytap mysys strings @mysql_se_unittest_dirs@ @mysql_pg_unittest_dirs@ ../dbug test: perl unit.pl run $(unittests) test-verbose: HARNESS_VERBOSE=1 perl unit.pl run $(unittests) - -# Don't update the files from bitkeeper -%::SCCS/s.% diff --git a/unittest/examples/Makefile.am b/unittest/examples/Makefile.am index a1627a58b4e..3e64c7ceddc 100644 --- a/unittest/examples/Makefile.am +++ b/unittest/examples/Makefile.am @@ -22,6 +22,3 @@ LDADD = -lmytap # We omit core-t here, since it will always fail. noinst_PROGRAMS = simple-t skip-t todo-t skip_all-t no_plan-t - -# Don't update the files from bitkeeper -%::SCCS/s.% diff --git a/unittest/mysys/CMakeLists.txt b/unittest/mysys/CMakeLists.txt new file mode 100644 index 00000000000..d8ce63f3c05 --- /dev/null +++ b/unittest/mysys/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (C) 2007 MySQL AB +# +# 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/unittest/mytap) +ADD_EXECUTABLE(bitmap-t bitmap-t.c) +TARGET_LINK_LIBRARIES(bitmap-t mytap mysys dbug strings wsock32) + +ADD_EXECUTABLE(base64-t base64-t.c) +TARGET_LINK_LIBRARIES(base64-t mytap mysys dbug strings wsock32) + +ADD_EXECUTABLE(my_atomic-t my_atomic-t.c) +TARGET_LINK_LIBRARIES(my_atomic-t mytap mysys dbug strings wsock32) + +ADD_EXECUTABLE(lf-t lf-t.c) +TARGET_LINK_LIBRARIES(lf-t mytap mysys dbug strings wsock32) + +ADD_EXECUTABLE(waiting_threads-t waiting_threads-t.c) +TARGET_LINK_LIBRARIES(waiting_threads-t mytap mysys dbug strings wsock32) diff --git a/unittest/mysys/Makefile.am b/unittest/mysys/Makefile.am index b195017e914..3a93f41afd1 100644 --- a/unittest/mysys/Makefile.am +++ b/unittest/mysys/Makefile.am @@ -13,15 +13,16 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include -AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap +INCLUDES = @ZLIB_INCLUDES@ -I$(top_builddir)/include \ + -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap + +noinst_HEADERS = thr_template.c LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ $(top_builddir)/mysys/libmysys.a \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/strings/libmystrings.a -noinst_PROGRAMS = bitmap-t base64-t - -# Don't update the files from bitkeeper -%::SCCS/s.% +EXTRA_DIST = CMakeLists.txt +noinst_PROGRAMS = bitmap-t base64-t my_atomic-t lf-t waiting_threads-t \ + my_vsnprintf-t ma_dyncol-t diff --git a/unittest/mysys/base64-t.c b/unittest/mysys/base64-t.c index e1a40f89ff0..cb0437f947c 100644 --- a/unittest/mysys/base64-t.c +++ b/unittest/mysys/base64-t.c @@ -66,6 +66,7 @@ main(void) ok(cmp == 0, "Comparing encode-decode result"); if (cmp != 0) { + /* FIXME: This only prints last value of the compared strings */ char buf[80]; diag(" --------- src --------- --------- dst ---------"); for (k= 0; k<src_len; k+=8) diff --git a/unittest/mysys/bitmap-t.c b/unittest/mysys/bitmap-t.c index 0bd21b63430..2065e10b53f 100644 --- a/unittest/mysys/bitmap-t.c +++ b/unittest/mysys/bitmap-t.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2006 MySQL AB +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 @@ -24,6 +24,8 @@ #include <tap.h> #include <m_string.h> +#define MAX_TESTED_BITMAP_SIZE 1024 + uint get_rand_bit(uint bitsize) { return (rand() % bitsize); @@ -75,11 +77,6 @@ error2: return TRUE; } -my_bool test_operators(MY_BITMAP *map __attribute__((unused)), - uint bitsize __attribute__((unused))) -{ - return FALSE; -} my_bool test_get_all_bits(MY_BITMAP *map, uint bitsize) { @@ -129,8 +126,8 @@ my_bool test_compare_operators(MY_BITMAP *map, uint bitsize) uint no_loops= bitsize > 128 ? 128 : bitsize; MY_BITMAP map2_obj, map3_obj; MY_BITMAP *map2= &map2_obj, *map3= &map3_obj; - uint32 map2buf[1024]; - uint32 map3buf[1024]; + my_bitmap_map map2buf[MAX_TESTED_BITMAP_SIZE]; + my_bitmap_map map3buf[MAX_TESTED_BITMAP_SIZE]; bitmap_init(&map2_obj, map2buf, bitsize, FALSE); bitmap_init(&map3_obj, map3buf, bitsize, FALSE); bitmap_clear_all(map2); @@ -257,8 +254,21 @@ error2: my_bool test_get_first_bit(MY_BITMAP *map, uint bitsize) { - uint i, test_bit; + uint i, test_bit= 0; uint no_loops= bitsize > 128 ? 128 : bitsize; + + bitmap_set_all(map); + for (i=0; i < bitsize; i++) + bitmap_clear_bit(map, i); + if (bitmap_get_first_set(map) != MY_BIT_NONE) + goto error1; + bitmap_clear_all(map); + for (i=0; i < bitsize; i++) + bitmap_set_bit(map, i); + if (bitmap_get_first(map) != MY_BIT_NONE) + goto error2; + bitmap_clear_all(map); + for (i=0; i < no_loops; i++) { test_bit=get_rand_bit(bitsize); @@ -321,6 +331,24 @@ my_bool test_prefix(MY_BITMAP *map, uint bitsize) goto error3; bitmap_clear_all(map); } + for (i=0; i < bitsize; i++) + { + if (bitmap_is_prefix(map, i + 1)) + goto error4; + bitmap_set_bit(map, i); + if (!bitmap_is_prefix(map, i + 1)) + goto error5; + test_bit=get_rand_bit(bitsize); + bitmap_set_bit(map, test_bit); + if (test_bit <= i && !bitmap_is_prefix(map, i + 1)) + goto error5; + else if (test_bit > i) + { + if (bitmap_is_prefix(map, i + 1)) + goto error4; + bitmap_clear_bit(map, test_bit); + } + } return FALSE; error1: diag("prefix1 error bitsize = %u, prefix_size = %u", bitsize,test_bit); @@ -331,13 +359,127 @@ error2: error3: diag("prefix3 error bitsize = %u, prefix_size = %u", bitsize,test_bit); return TRUE; +error4: + diag("prefix4 error bitsize = %u, i = %u", bitsize,i); + return TRUE; +error5: + diag("prefix5 error bitsize = %u, i = %u", bitsize,i); + return TRUE; } +my_bool test_compare(MY_BITMAP *map, uint bitsize) +{ + MY_BITMAP map2; + uint32 map2buf[MAX_TESTED_BITMAP_SIZE]; + uint i, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + if (bitmap_init(&map2, map2buf, bitsize, FALSE)) + { + diag("init error for bitsize %d", bitsize); + return TRUE; + } + /* Test all 4 possible combinations of set/unset bits. */ + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + bitmap_clear_bit(map, test_bit); + bitmap_clear_bit(&map2, test_bit); + if (!bitmap_is_subset(map, &map2)) + goto error_is_subset; + bitmap_set_bit(map, test_bit); + if (bitmap_is_subset(map, &map2)) + goto error_is_subset; + bitmap_set_bit(&map2, test_bit); + if (!bitmap_is_subset(map, &map2)) + goto error_is_subset; + bitmap_clear_bit(map, test_bit); + if (!bitmap_is_subset(map, &map2)) + goto error_is_subset; + /* Note that test_bit is not cleared i map2. */ + } + bitmap_clear_all(map); + bitmap_clear_all(&map2); + /* Test all 4 possible combinations of set/unset bits. */ + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + if (bitmap_is_overlapping(map, &map2)) + goto error_is_overlapping; + bitmap_set_bit(map, test_bit); + if (bitmap_is_overlapping(map, &map2)) + goto error_is_overlapping; + bitmap_set_bit(&map2, test_bit); + if (!bitmap_is_overlapping(map, &map2)) + goto error_is_overlapping; + bitmap_clear_bit(map, test_bit); + if (bitmap_is_overlapping(map, &map2)) + goto error_is_overlapping; + bitmap_clear_bit(&map2, test_bit); + /* Note that test_bit is not cleared i map2. */ + } + return FALSE; +error_is_subset: + diag("is_subset error bitsize = %u", bitsize); + return TRUE; +error_is_overlapping: + diag("is_overlapping error bitsize = %u", bitsize); + return TRUE; +} + +my_bool test_intersect(MY_BITMAP *map, uint bitsize) +{ + uint bitsize2 = 1 + get_rand_bit(MAX_TESTED_BITMAP_SIZE - 1); + MY_BITMAP map2; + uint32 map2buf[MAX_TESTED_BITMAP_SIZE]; + uint i, test_bit1, test_bit2, test_bit3; + if (bitmap_init(&map2, map2buf, bitsize2, FALSE)) + { + diag("init error for bitsize %d", bitsize2); + return TRUE; + } + test_bit1= get_rand_bit(bitsize); + test_bit2= get_rand_bit(bitsize); + bitmap_set_bit(map, test_bit1); + bitmap_set_bit(map, test_bit2); + test_bit3= get_rand_bit(bitsize2); + bitmap_set_bit(&map2, test_bit3); + if (test_bit2 < bitsize2) + bitmap_set_bit(&map2, test_bit2); + + bitmap_intersect(map, &map2); + if (test_bit2 < bitsize2) + { + if (!bitmap_is_set(map, test_bit2)) + goto error; + bitmap_clear_bit(map, test_bit2); + } + if (test_bit1 == test_bit3) + { + if (!bitmap_is_set(map, test_bit1)) + goto error; + bitmap_clear_bit(map, test_bit1); + } + if (!bitmap_is_clear_all(map)) + goto error; + + bitmap_set_all(map); + bitmap_set_all(&map2); + for (i=0; i < bitsize2; i++) + bitmap_clear_bit(&map2, i); + bitmap_intersect(map, &map2); + if (!bitmap_is_clear_all(map)) + goto error; + return FALSE; +error: + diag("intersect error bitsize = %u, bit1 = %u, bit2 = %u, bit3 = %u", + bitsize, test_bit1, test_bit2, test_bit3); + return TRUE; +} my_bool do_test(uint bitsize) { MY_BITMAP map; - uint32 buf[1024]; + my_bitmap_map buf[MAX_TESTED_BITMAP_SIZE]; if (bitmap_init(&map, buf, bitsize, FALSE)) { diag("init error for bitsize %d", bitsize); @@ -349,9 +491,6 @@ my_bool do_test(uint bitsize) if (test_flip_bit(&map,bitsize)) goto error; bitmap_clear_all(&map); - if (test_operators(&map,bitsize)) - goto error; - bitmap_clear_all(&map); if (test_get_all_bits(&map, bitsize)) goto error; bitmap_clear_all(&map); @@ -366,8 +505,15 @@ my_bool do_test(uint bitsize) bitmap_clear_all(&map); if (test_get_next_bit(&map,bitsize)) goto error; + bitmap_clear_all(&map); if (test_prefix(&map,bitsize)) goto error; + bitmap_clear_all(&map); + if (test_compare(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_intersect(&map,bitsize)) + goto error; return FALSE; error: return TRUE; @@ -377,11 +523,17 @@ int main() { int i; int const min_size = 1; - int const max_size = 1024; + int const max_size = MAX_TESTED_BITMAP_SIZE; MY_INIT("bitmap-t"); - plan(max_size - min_size); - for (i= min_size; i < max_size; i++) + plan((max_size - min_size)/7+1); + + /* + It's ok to do steps in 7, as i module 64 will go trough all values 1..63. + Any errors in the code should manifest as we are working with integers + of size 16, 32, or 64 bits... + */ + for (i= min_size; i < max_size; i+=7) ok(do_test(i) == 0, "bitmap size %d", i); return exit_status(); } diff --git a/unittest/mysys/lf-t.c b/unittest/mysys/lf-t.c new file mode 100644 index 00000000000..61b7ae08cf5 --- /dev/null +++ b/unittest/mysys/lf-t.c @@ -0,0 +1,178 @@ +/* Copyright (C) 2008-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + 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; version 2 of the License. + + 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 */ + +/** + @file + + Unit tests for lock-free algorithms of mysys +*/ + +#include "thr_template.c" + +#include <lf.h> + +int32 inserts= 0, N; +LF_ALLOCATOR lf_allocator; +LF_HASH lf_hash; + +/* + pin allocator - alloc and release an element in a loop +*/ +pthread_handler_t test_lf_pinbox(void *arg) +{ + int m= *(int *)arg; + int32 x= 0; + LF_PINS *pins; + + my_thread_init(); + + pins= lf_pinbox_get_pins(&lf_allocator.pinbox); + + for (x= ((int)(intptr)(&m)); m ; m--) + { + lf_pinbox_put_pins(pins); + pins= lf_pinbox_get_pins(&lf_allocator.pinbox); + } + lf_pinbox_put_pins(pins); + pthread_mutex_lock(&mutex); + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + my_thread_end(); + return 0; +} + +/* + thread local data area, allocated using lf_alloc. + union is required to enforce the minimum required element size (sizeof(ptr)) +*/ +typedef union { + int32 data; + void *not_used; +} TLA; + +pthread_handler_t test_lf_alloc(void *arg) +{ + int m= (*(int *)arg)/2; + int32 x,y= 0; + LF_PINS *pins; + + my_thread_init(); + + pins= lf_alloc_get_pins(&lf_allocator); + + for (x= ((int)(intptr)(&m)); m ; m--) + { + TLA *node1, *node2; + x= (x*m+0x87654321) & INT_MAX32; + node1= (TLA *)lf_alloc_new(pins); + node1->data= x; + y+= node1->data; + node1->data= 0; + node2= (TLA *)lf_alloc_new(pins); + node2->data= x; + y-= node2->data; + node2->data= 0; + lf_alloc_free(pins, node1); + lf_alloc_free(pins, node2); + } + lf_alloc_put_pins(pins); + pthread_mutex_lock(&mutex); + bad+= y; + + if (--N == 0) + { + diag("%d mallocs, %d pins in stack", + lf_allocator.mallocs, lf_allocator.pinbox.pins_in_array); +#ifdef MY_LF_EXTRA_DEBUG + bad|= lf_allocator.mallocs - lf_alloc_pool_count(&lf_allocator); +#endif + } + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + my_thread_end(); + return 0; +} + +#define N_TLH 1000 +pthread_handler_t test_lf_hash(void *arg) +{ + int m= (*(int *)arg)/(2*N_TLH); + int32 x,y,z,sum= 0, ins= 0; + LF_PINS *pins; + + my_thread_init(); + + pins= lf_hash_get_pins(&lf_hash); + + for (x= ((int)(intptr)(&m)); m ; m--) + { + int i; + y= x; + for (i= 0; i < N_TLH; i++) + { + x= (x*(m+i)+0x87654321) & INT_MAX32; + z= (x<0) ? -x : x; + if (lf_hash_insert(&lf_hash, pins, &z)) + { + sum+= z; + ins++; + } + } + for (i= 0; i < N_TLH; i++) + { + y= (y*(m+i)+0x87654321) & INT_MAX32; + z= (y<0) ? -y : y; + if (lf_hash_delete(&lf_hash, pins, (uchar *)&z, sizeof(z))) + sum-= z; + } + } + lf_hash_put_pins(pins); + pthread_mutex_lock(&mutex); + bad+= sum; + inserts+= ins; + + if (--N == 0) + { + diag("%d mallocs, %d pins in stack, %d hash size, %d inserts", + lf_hash.alloc.mallocs, lf_hash.alloc.pinbox.pins_in_array, + lf_hash.size, inserts); + bad|= lf_hash.count; + } + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + my_thread_end(); + return 0; +} + + +void do_tests() +{ + plan(4); + + lf_alloc_init(&lf_allocator, sizeof(TLA), offsetof(TLA, not_used)); + lf_hash_init(&lf_hash, sizeof(int), LF_HASH_UNIQUE, 0, sizeof(int), 0, + &my_charset_bin); + + bad= my_atomic_initialize(); + ok(!bad, "my_atomic_initialize() returned %d", bad); + + test_concurrently("lf_pinbox", test_lf_pinbox, N= THREADS, CYCLES); + test_concurrently("lf_alloc", test_lf_alloc, N= THREADS, CYCLES); + test_concurrently("lf_hash", test_lf_hash, N= THREADS, CYCLES/10); + + lf_hash_destroy(&lf_hash); + lf_alloc_destroy(&lf_allocator); +} + diff --git a/unittest/mysys/ma_dyncol-t.c b/unittest/mysys/ma_dyncol-t.c new file mode 100644 index 00000000000..0e973b20747 --- /dev/null +++ b/unittest/mysys/ma_dyncol-t.c @@ -0,0 +1,795 @@ +/* Copyright (c) 2011, Monty Program Ab + Copyright (c) 2011, Oleksandr Byelkin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#include <my_global.h> +#include <my_sys.h> +#include <m_string.h> +#include <ma_dyncol.h> +#include <tap.h> + +void test_value_single_null() +{ + int rc= FALSE; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_NULL; + dynamic_column_value_init(&res); + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= (res.type == DYN_COL_NULL); +err: + ok(rc, "%s", "NULL"); + /* cleanup */ + dynamic_column_column_free(&str); +} + +void test_value_single_uint(ulonglong num, const char *name) +{ + int rc= FALSE; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_UINT; + val.ulong_value= num; + dynamic_column_value_init(&res); + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= (res.type == DYN_COL_UINT) && (res.ulong_value == num); + num= res.ulong_value; +err: + ok(rc, "%s - %llu", name, num); + /* cleanup */ + dynamic_column_column_free(&str); +} + +void test_value_single_sint(longlong num, const char *name) +{ + int rc= FALSE; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_INT; + val.long_value= num; + dynamic_column_value_init(&res); + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= (res.type == DYN_COL_INT) && (res.long_value == num); + num= res.ulong_value; +err: + ok(rc, "%s - %lld", name, num); + /* cleanup */ + dynamic_column_column_free(&str); +} + + +void test_value_single_double(double num, const char *name) +{ + int rc= FALSE; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_DOUBLE; + val.double_value= num; + dynamic_column_value_init(&res); + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= (res.type == DYN_COL_DOUBLE) && (res.double_value == num); + num= res.ulong_value; +err: + ok(rc, "%s - %lf", name, num); + /* cleanup */ + dynamic_column_column_free(&str); +} + +void test_value_single_decimal(const char *num) +{ + char *end= (((char*)num) + strlen(num)); + char buff[80]; + int rc= FALSE; + int length= 80; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + + /* init values */ + dynamic_column_prepare_decimal(&val); // special procedure for decimal!!! + if (string2decimal(num, &val.decimal_value, &end) != E_DEC_OK) + goto err; + dynamic_column_value_init(&res); + + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_DECIMAL) && + (decimal_cmp(&res.decimal_value, &val.decimal_value) == 0)); + decimal2string(&res.decimal_value, buff, &length, 0, 0, ' '); +err: + ok(rc, "%s - %s", num, buff); + /* cleanup */ + dynamic_column_column_free(&str); +} + +static CHARSET_INFO *charset_list[]= +{ +#ifdef HAVE_CHARSET_big5 + &my_charset_big5_chinese_ci, + &my_charset_big5_bin, +#endif +#ifdef HAVE_CHARSET_euckr + &my_charset_euckr_korean_ci, + &my_charset_euckr_bin, +#endif +#ifdef HAVE_CHARSET_gb2312 + &my_charset_gb2312_chinese_ci, + &my_charset_gb2312_bin, +#endif +#ifdef HAVE_CHARSET_gbk + &my_charset_gbk_chinese_ci, + &my_charset_gbk_bin, +#endif +#ifdef HAVE_CHARSET_latin1 + &my_charset_latin1, + &my_charset_latin1_bin, +#endif +#ifdef HAVE_CHARSET_sjis + &my_charset_sjis_japanese_ci, + &my_charset_sjis_bin, +#endif +#ifdef HAVE_CHARSET_tis620 + &my_charset_tis620_thai_ci, + &my_charset_tis620_bin, +#endif +#ifdef HAVE_CHARSET_ujis + &my_charset_ujis_japanese_ci, + &my_charset_ujis_bin, +#endif +#ifdef HAVE_CHARSET_utf8 + &my_charset_utf8_general_ci, +#ifdef HAVE_HAVE_UCA_COLLATIONS + &my_charset_utf8_unicode_ci, +#endif + &my_charset_utf8_bin, +#endif +}; + + +void test_value_single_string(const char *string, size_t len, + CHARSET_INFO *cs) +{ + int rc= FALSE; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + + /* init values */ + val.type= DYN_COL_STRING; + val.string_value.str= (char*)string; + val.string_value.length= len; + val.charset= cs; + dynamic_column_value_init(&res); + + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_STRING) && + (res.string_value.length == len) && + (memcmp(res.string_value.str, string, len) == 0) && + (res.charset->number == cs->number)); +err: + ok(rc, "'%s' - '%s' %u %u-%s", string, + res.string_value.str, (uint)res.string_value.length, + (uint)res.charset->number, res.charset->name); + /* cleanup */ + val.string_value.str= NULL; // we did not allocated it + dynamic_column_column_free(&str); +} + +void test_value_single_date(uint year, uint month, uint day, const char *name) +{ + int rc= FALSE; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_DATE; + val.time_value.time_type= MYSQL_TIMESTAMP_DATE; + val.time_value.year= year; + val.time_value.month= month; + val.time_value.day= day; + dynamic_column_value_init(&res); + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_DATE) && + (res.time_value.time_type == MYSQL_TIMESTAMP_DATE) && + (res.time_value.year == year) && + (res.time_value.month == month) && + (res.time_value.day == day)); +err: + ok(rc, "%s - %04u-%02u-%02u", name, year, month, day); + /* cleanup */ + dynamic_column_column_free(&str); +} + +void test_value_single_time(uint neg, uint hour, uint minute, uint second, + uint mic, const char *name) +{ + int rc= FALSE; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_TIME; + val.time_value.time_type= MYSQL_TIMESTAMP_TIME; + val.time_value.neg= neg; + val.time_value.hour= hour; + val.time_value.minute= minute; + val.time_value.second= second; + val.time_value.second_part= mic; + dynamic_column_value_init(&res); + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_TIME) && + (res.time_value.time_type == MYSQL_TIMESTAMP_TIME) && + (res.time_value.neg == (int)neg) && + (res.time_value.hour == hour) && + (res.time_value.minute == minute) && + (res.time_value.second == second) && + (res.time_value.second_part == mic)); +err: + ok(rc, "%s - %c%02u:%02u:%02u.%06u", name, (neg ? '-' : '+'), + hour, minute, second, mic); + /* cleanup */ + dynamic_column_column_free(&str); +} + + +void test_value_single_datetime(uint neg, uint year, uint month, uint day, + uint hour, uint minute, uint second, + uint mic, const char *name) +{ + int rc= FALSE; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_DATETIME; + val.time_value.time_type= MYSQL_TIMESTAMP_DATETIME; + val.time_value.neg= neg; + val.time_value.year= year; + val.time_value.month= month; + val.time_value.day= day; + val.time_value.hour= hour; + val.time_value.minute= minute; + val.time_value.second= second; + val.time_value.second_part= mic; + dynamic_column_value_init(&res); + /* create column */ + if (dynamic_column_create(&str, 1, &val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (dynamic_column_get(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_DATETIME) && + (res.time_value.time_type == MYSQL_TIMESTAMP_DATETIME) && + (res.time_value.neg == (int)neg) && + (res.time_value.year == year) && + (res.time_value.month == month) && + (res.time_value.day == day) && + (res.time_value.hour == hour) && + (res.time_value.minute == minute) && + (res.time_value.second == second) && + (res.time_value.second_part == mic)); +err: + ok(rc, "%s - %c %04u-%02u-%02u %02u:%02u:%02u.%06u", name, (neg ? '-' : '+'), + year, month, day, hour, minute, second, mic); + /* cleanup */ + dynamic_column_column_free(&str); +} + + +void test_value_multi(ulonglong num0, + longlong num1, + double num2, + const char *num3, + const char *string4, size_t len4, CHARSET_INFO *cs4, + uint year5, uint month5, uint day5, + uint neg6, uint hour6, uint minute6, + uint second6, uint mic6, + uint neg7, uint year7, uint month7, uint day7, + uint hour7, uint minute7, uint second7, + uint mic7, + uint *column_numbers, + const char *name) +{ + char *end3= (((char*)num3) + strlen(num3)); + int rc= FALSE; + uint i; + DYNAMIC_COLUMN_VALUE val[9], res[9]; + DYNAMIC_COLUMN str; + /* init values */ + val[0].type= DYN_COL_UINT; + val[0].ulong_value= num0; + val[1].type= DYN_COL_INT; + val[1].long_value= num1; + val[2].type= DYN_COL_DOUBLE; + val[2].double_value= num2; + dynamic_column_prepare_decimal(val + 3); // special procedure for decimal!!! + if (string2decimal(num3, &val[3].decimal_value, &end3) != E_DEC_OK) + goto err; + val[4].type= DYN_COL_STRING; + val[4].string_value.str= (char*)string4; + val[4].string_value.length= len4; + val[4].charset= cs4; + val[5].type= DYN_COL_DATE; + val[5].time_value.time_type= MYSQL_TIMESTAMP_DATE; + val[5].time_value.year= year5; + val[5].time_value.month= month5; + val[5].time_value.day= day5; + val[6].type= DYN_COL_TIME; + val[6].time_value.time_type= MYSQL_TIMESTAMP_TIME; + val[6].time_value.neg= neg6; + val[6].time_value.hour= hour6; + val[6].time_value.minute= minute6; + val[6].time_value.second= second6; + val[6].time_value.second_part= mic6; + val[7].type= DYN_COL_DATETIME; + val[7].time_value.time_type= MYSQL_TIMESTAMP_DATETIME; + val[7].time_value.neg= neg7; + val[7].time_value.year= year7; + val[7].time_value.month= month7; + val[7].time_value.day= day7; + val[7].time_value.hour= hour7; + val[7].time_value.minute= minute7; + val[7].time_value.second= second7; + val[7].time_value.second_part= mic7; + val[8].type= DYN_COL_NULL; + for (i= 0; i < 9; i++) + dynamic_column_value_init(res + i); + /* create column */ + if (dynamic_column_create_many(&str, 9, column_numbers, val)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + for (i= 0; i < 9; i++) + if (dynamic_column_get(&str, column_numbers[i], res + i)) + goto err; + rc= ((res[0].type == DYN_COL_UINT) && + (res[0].ulong_value == num0) && + (res[1].type == DYN_COL_INT) && + (res[1].long_value == num1) && + (res[2].type == DYN_COL_DOUBLE) && + (res[2].double_value == num2) && + (res[3].type == DYN_COL_DECIMAL) && + (decimal_cmp(&res[3].decimal_value, &val[3].decimal_value) == 0) && + (res[4].type == DYN_COL_STRING) && + (res[4].string_value.length == len4) && + (memcmp(res[4].string_value.str, string4, len4) == 0) && + (res[4].charset->number == cs4->number) && + (res[5].type == DYN_COL_DATE) && + (res[5].time_value.time_type == MYSQL_TIMESTAMP_DATE) && + (res[5].time_value.year == year5) && + (res[5].time_value.month == month5) && + (res[5].time_value.day == day5) && + (res[6].type == DYN_COL_TIME) && + (res[6].time_value.time_type == MYSQL_TIMESTAMP_TIME) && + (res[6].time_value.neg == (int)neg6) && + (res[6].time_value.hour == hour6) && + (res[6].time_value.minute == minute6) && + (res[6].time_value.second == second6) && + (res[6].time_value.second_part == mic6) && + (res[7].type == DYN_COL_DATETIME) && + (res[7].time_value.time_type == MYSQL_TIMESTAMP_DATETIME) && + (res[7].time_value.neg == (int)neg7) && + (res[7].time_value.year == year7) && + (res[7].time_value.month == month7) && + (res[7].time_value.day == day7) && + (res[7].time_value.hour == hour7) && + (res[7].time_value.minute == minute7) && + (res[7].time_value.second == second7) && + (res[7].time_value.second_part == mic7) && + (res[8].type == DYN_COL_NULL)); +err: + ok(rc, "%s", name); + /* cleanup */ + val[4].string_value.str= NULL; // we did not allocated it + dynamic_column_column_free(&str); +} + + +void test_value_multi_same_num() +{ + int rc= FALSE; + uint i; + DYNAMIC_COLUMN_VALUE val[5]; + uint column_numbers[]= {3,4,5,3,6}; // same column numbers + DYNAMIC_COLUMN str; + /* init values */ + for (i= 0; i < 5; i++) + val[i].type= DYN_COL_NULL; + /* create column */ + if (!dynamic_column_create_many(&str, 5, column_numbers, val)) + goto err; + rc= TRUE; +err: + ok(rc, "%s", "same column numbers check"); + /* cleanup */ + dynamic_column_column_free(&str); +} + + +void test_update_multi(uint *column_numbers, uint *column_values, + my_bool *null_values, int only_add, int all) +{ + int rc= FALSE; + int i, j; + DYNAMIC_COLUMN str; + DYNAMIC_COLUMN_VALUE val; + + val.type= DYN_COL_UINT; + val.ulong_value= column_values[0]; + if (dynamic_column_create(&str, column_numbers[0], &val)) + goto err; + for (i= 1; i < all; i++) + { + val.type= (null_values[i] ? DYN_COL_NULL : DYN_COL_UINT); + val.ulong_value= column_values[i]; + if (dynamic_column_update(&str, column_numbers[i], &val)) + goto err; + + /* check value(s) */ + for (j= i; j >= (i < only_add ? 0 : i); j--) + { + if (dynamic_column_get(&str, column_numbers[j], &val)) + goto err; + if (null_values[j]) + { + if (val.type != DYN_COL_NULL || + dynamic_column_exists(&str, column_numbers[j]) == ER_DYNCOL_YES) + goto err; + } + else + { + if (val.type != DYN_COL_UINT || + val.ulong_value != column_values[j] || + dynamic_column_exists(&str, column_numbers[j]) == ER_DYNCOL_NO) + goto err; + } + } + if (i < only_add) + { + DYNAMIC_ARRAY num; + if (dynamic_column_list(&str, &num)) + goto err; + /* cross check arrays */ + if ((int)num.elements != i + 1) + { + delete_dynamic(&num); + goto err; + } + for(j= 0; j < i + 1; j++) + { + int k; + for(k= 0; + k < i + 1 && column_numbers[j] != + *dynamic_element(&num, k, uint*); + k++); + if (k >= i + 1) + { + delete_dynamic(&num); + goto err; + } + for(k= 0; + k < i + 1 && column_numbers[k] != + *dynamic_element(&num, j, uint*); + k++); + if (k >= i + 1) + { + delete_dynamic(&num); + goto err; + } + } + delete_dynamic(&num); + } + } + + rc= TRUE; +err: + ok(rc, "%s", "add/delete/update"); + /* cleanup */ + dynamic_column_column_free(&str); +} + +void test_empty_string() +{ + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + DYNAMIC_ARRAY array_of_uint; + int rc; + /* empty string */ + bzero(&str, sizeof(str)); + + rc= dynamic_column_get(&str, 1, &res); + ok( (rc == ER_DYNCOL_OK) && (res.type == DYN_COL_NULL), "%s", "empty get"); + + rc= dynamic_column_delete(&str, 1); + ok( (rc == ER_DYNCOL_OK), "%s", "empty delete"); + + rc= dynamic_column_exists(&str, 1); + ok( (rc == ER_DYNCOL_NO), "%s", "empty exists"); + + rc= dynamic_column_list(&str, &array_of_uint); + ok( (rc == ER_DYNCOL_OK) && (array_of_uint.elements == 0), + "%s", "empty list"); + + val.type= DYN_COL_UINT; + val.ulong_value= 1212; + rc= dynamic_column_update(&str, 1, &val); + if (rc == ER_DYNCOL_OK) + rc= dynamic_column_get(&str, 1, &res); + ok( (rc == ER_DYNCOL_OK) && + (res.type == DYN_COL_UINT) && (res.ulong_value == val.ulong_value), + "%s", "empty update"); +} + + +void test_update_many(uint *column_numbers, uint *column_values, + uint column_count, + uint *update_numbers, uint *update_values, + my_bool *update_nulls, uint update_count, + uint *result_numbers, uint *result_values, + uint result_count) +{ + int rc= FALSE; + uint i; + DYNAMIC_COLUMN str1; + DYNAMIC_COLUMN str2; + DYNAMIC_COLUMN_VALUE *val, *upd, *res; + + val= (DYNAMIC_COLUMN_VALUE *)malloc(sizeof(DYNAMIC_COLUMN_VALUE) * + column_count); + upd= (DYNAMIC_COLUMN_VALUE *)malloc(sizeof(DYNAMIC_COLUMN_VALUE) * + update_count); + res= (DYNAMIC_COLUMN_VALUE *)malloc(sizeof(DYNAMIC_COLUMN_VALUE) * + result_count); + + + for (i= 0; i < column_count; i++) + { + val[i].type= DYN_COL_UINT; + val[i].ulong_value= column_values[i]; + } + for (i= 0; i < update_count; i++) + { + if (update_nulls[i]) + upd[i].type= DYN_COL_NULL; + else + { + upd[i].type= DYN_COL_UINT; + upd[i].ulong_value= update_values[i]; + } + } + for (i= 0; i < result_count; i++) + { + res[i].type= DYN_COL_UINT; + res[i].ulong_value= result_values[i]; + } + if (dynamic_column_create_many(&str1, column_count, column_numbers, val)) + goto err; + if (dynamic_column_update_many(&str1, update_count, update_numbers, upd)) + goto err; + if (dynamic_column_create_many(&str2, result_count, result_numbers, res)) + goto err; + if (str1.length == str2.length && + memcmp(str1.str, str2.str, str1.length) ==0) + rc= TRUE; + +err: + ok(rc, "%s", "update_many"); + /* cleanup */ + dynamic_column_column_free(&str1); + dynamic_column_column_free(&str2); +} + +int main(int argc __attribute__((unused)), char **argv) +{ + uint i; + char *big_string= (char *)malloc(1024*1024); + + MY_INIT(argv[0]); + plan(60); + + if (!big_string) + exit(1); + for (i= 0; i < 1024*1024; i++) + big_string[i]= ('0' + (i % 10)); + test_value_single_null(); + test_value_single_uint(0, "0"); + test_value_single_uint(ULL(0xffffffffffffffff), "0xffffffffffffffff"); + test_value_single_uint(ULL(0xaaaaaaaaaaaaaaaa), "0xaaaaaaaaaaaaaaaa"); + test_value_single_uint(ULL(0x5555555555555555), "0x5555555555555555"); + test_value_single_uint(27652, "27652"); + test_value_single_sint(0, "0"); + test_value_single_sint(1, "1"); + test_value_single_sint(-1, "-1"); + test_value_single_sint(LL(0x7fffffffffffffff), + "0x7fffffffffffffff"); + test_value_single_sint(LL(0xaaaaaaaaaaaaaaaa), + "0xaaaaaaaaaaaaaaaa"); + test_value_single_sint(LL(0x5555555555555555), + "0x5555555555555555"); + test_value_single_sint(LL(0x8000000000000000), + "0x8000000000000000"); + test_value_single_double(0.0, "0.0"); + test_value_single_double(1.0, "1.0"); + test_value_single_double(-1.0, "-1.0"); + test_value_single_double(1.0e100, "1.0e100"); + test_value_single_double(1.0e-100, "1.0e-100"); + test_value_single_double(9999999999999999999999999999999999999.0, + "9999999999999999999999999999999999999.0"); + test_value_single_double(-9999999999999999999999999999999999999.0, + "-9999999999999999999999999999999999999.0"); + test_value_single_decimal("0"); + test_value_single_decimal("1"); + test_value_single_decimal("-1"); + test_value_single_decimal("9999999999999999999999999999999"); + test_value_single_decimal("-9999999999999999999999999999999"); + test_value_single_decimal("0.9999999999999999999999999999999"); + test_value_single_decimal("-0.9999999999999999999999999999999"); + test_value_single_string("", 0, charset_list[0]); + test_value_single_string("", 1, charset_list[0]); + test_value_single_string("1234567890", 11, charset_list[0]); + test_value_single_string("nulls\0\0\0\0\0", 10, charset_list[0]); + sprintf(big_string, "%x", 0x7a); + test_value_single_string(big_string, 0x7a, charset_list[0]); + sprintf(big_string, "%x", 0x80); + test_value_single_string(big_string, 0x80, charset_list[0]); + sprintf(big_string, "%x", 0x7ffa); + test_value_single_string(big_string, 0x7ffa, charset_list[0]); + sprintf(big_string, "%x", 0x8000); + test_value_single_string(big_string, 0x8000, charset_list[0]); + sprintf(big_string, "%x", 1024*1024); + test_value_single_string(big_string, 1024*1024, charset_list[0]); + test_value_single_date(0, 0, 0, "zero date"); + test_value_single_date(9999, 12, 31, "max date"); + test_value_single_date(2011, 3, 26, "some date"); + test_value_single_time(0, 0, 0, 0, 0, "zero time"); + test_value_single_time(1, 23, 59, 59, 999999, "min time"); + test_value_single_time(0, 23, 59, 59, 999999, "max time"); + test_value_single_time(0, 21, 36, 20, 28, "some time"); + test_value_single_datetime(0, 0, 0, 0, 0, 0, 0, 0, "zero datetime"); + test_value_single_datetime(1, 9999, 12, 31, 23, 59, 59, 999999, + "min datetime"); + test_value_single_datetime(0, 9999, 12, 31, 23, 59, 59, 999999, + "max datetime"); + test_value_single_datetime(0, 2011, 3, 26, 21, 53, 12, 3445, + "some datetime"); + { + uint column_numbers[]= {100,1,2,3,4,5,6,7,8}; + test_value_multi(0, 0, 0.0, "0", + "", 0, charset_list[0], + 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + column_numbers, + "zero data"); + } + { + uint column_numbers[]= {10,1,12,37,4,57,6,76,87}; + test_value_multi(ULL(0xffffffffffffffff), LL(0x7fffffffffffffff), + 99999999.999e120, "9999999999999999999999999999999", + big_string, 1024*1024, charset_list[0], + 9999, 12, 31, + 0, 23, 59, 59, 999999, + 0, 9999, 12, 31, 23, 59, 59, 999999, + column_numbers, + "much data"); + } + free(big_string); + { + uint column_numbers[]= {101,12,122,37,24,572,16,726,77}; + test_value_multi(37878, -3344, + 2873.3874, "92743.238984789898", + "string", 6, charset_list[0], + 2011, 3, 26, + 1, 23, 23, 20, 333, + 0, 2011, 3, 26, 23, 23, 53, 334, + column_numbers, + "zero data"); + } + test_value_multi_same_num(); + { + uint column_numbers[]= {1,2,3,4,5,6,7,2, 3, 4}; + uint column_values[]= {1,2,3,4,5,6,7,0,30,40}; + my_bool null_values[]= {0,0,0,0,0,0,0,1, 0, 0}; + + test_update_multi(column_numbers, column_values, null_values, 7, 10); + } + { + uint column_numbers[]= {4,3,2,1, 1,2,3,4}; + uint column_values[]= {4,3,2,1, 0,0,0,0}; + my_bool null_values[]= {0,0,0,0, 1,1,1,1}; + + test_update_multi(column_numbers, column_values, null_values, 4, 8); + } + { + uint column_numbers[]= {4,3,2,1, 4,3,2,1}; + uint column_values[]= {4,3,2,1, 0,0,0,0}; + my_bool null_values[]= {0,0,0,0, 1,1,1,1}; + + test_update_multi(column_numbers, column_values, null_values, 4, 8); + } + test_empty_string(); + { + uint column_numbers[]= {1, 2, 3}; + uint column_values[]= {1, 2, 3}; + uint update_numbers[]= {4, 3, 2, 1}; + uint update_values[]= {40,30, 0,10}; + my_bool update_nulls[]={0, 0, 1, 0}; + uint result_numbers[]= {1, 3, 4}; + uint result_values[]= {10,30,40}; + test_update_many(column_numbers, column_values, 3, + update_numbers, update_values, update_nulls, 4, + result_numbers, result_values, 3); + } + return exit_status(); +} diff --git a/unittest/mysys/my_atomic-t.c b/unittest/mysys/my_atomic-t.c new file mode 100644 index 00000000000..b98f3379a4a --- /dev/null +++ b/unittest/mysys/my_atomic-t.c @@ -0,0 +1,146 @@ +/* Copyright (C) 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc. + + 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; version 2 of the License. + + 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 "thr_template.c" + +/* at least gcc 3.4.5 and 3.4.6 (but not 3.2.3) on RHEL */ +#if __GNUC__ == 3 && __GNUC_MINOR__ == 4 +#define GCC_BUG_WORKAROUND volatile +#else +#define GCC_BUG_WORKAROUND +#endif + +volatile uint32 b32; +volatile int32 c32; +my_atomic_rwlock_t rwl; + +/* add and sub a random number in a loop. Must get 0 at the end */ +pthread_handler_t test_atomic_add(void *arg) +{ + int m= (*(int *)arg)/2; + GCC_BUG_WORKAROUND int32 x; + for (x= ((int)(intptr)(&m)); m ; m--) + { + x= (x*m+0x87654321) & INT_MAX32; + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&bad, x); + my_atomic_rwlock_wrunlock(&rwl); + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&bad, -x); + my_atomic_rwlock_wrunlock(&rwl); + } + pthread_mutex_lock(&mutex); + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +/* + 1. generate thread number 0..N-1 from b32 + 2. add it to bad + 3. swap thread numbers in c32 + 4. (optionally) one more swap to avoid 0 as a result + 5. subtract result from bad + must get 0 in bad at the end +*/ +pthread_handler_t test_atomic_fas(void *arg) +{ + int m= *(int *)arg; + int32 x; + + my_atomic_rwlock_wrlock(&rwl); + x= my_atomic_add32(&b32, 1); + my_atomic_rwlock_wrunlock(&rwl); + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&bad, x); + my_atomic_rwlock_wrunlock(&rwl); + + for (; m ; m--) + { + my_atomic_rwlock_wrlock(&rwl); + x= my_atomic_fas32(&c32, x); + my_atomic_rwlock_wrunlock(&rwl); + } + + if (!x) + { + my_atomic_rwlock_wrlock(&rwl); + x= my_atomic_fas32(&c32, x); + my_atomic_rwlock_wrunlock(&rwl); + } + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&bad, -x); + my_atomic_rwlock_wrunlock(&rwl); + + pthread_mutex_lock(&mutex); + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +/* + same as test_atomic_add, but my_atomic_add32 is emulated with + my_atomic_cas32 - notice that the slowdown is proportional to the + number of CPUs +*/ +pthread_handler_t test_atomic_cas(void *arg) +{ + int m= (*(int *)arg)/2, ok= 0; + GCC_BUG_WORKAROUND int32 x, y; + for (x= ((int)(intptr)(&m)); m ; m--) + { + my_atomic_rwlock_wrlock(&rwl); + y= my_atomic_load32(&bad); + my_atomic_rwlock_wrunlock(&rwl); + x= (x*m+0x87654321) & INT_MAX32; + do { + my_atomic_rwlock_wrlock(&rwl); + ok= my_atomic_cas32(&bad, &y, (uint32)y+x); + my_atomic_rwlock_wrunlock(&rwl); + } while (!ok) ; + do { + my_atomic_rwlock_wrlock(&rwl); + ok= my_atomic_cas32(&bad, &y, y-x); + my_atomic_rwlock_wrunlock(&rwl); + } while (!ok) ; + } + pthread_mutex_lock(&mutex); + if (!--running_threads) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +void do_tests() +{ + plan(4); + + bad= my_atomic_initialize(); + ok(!bad, "my_atomic_initialize() returned %d", bad); + + my_atomic_rwlock_init(&rwl); + + b32= c32= 0; + test_concurrently("my_atomic_add32", test_atomic_add, THREADS, CYCLES); + b32= c32= 0; + test_concurrently("my_atomic_fas32", test_atomic_fas, THREADS, CYCLES); + b32= c32= 0; + test_concurrently("my_atomic_cas32", test_atomic_cas, THREADS, CYCLES); + + my_atomic_rwlock_destroy(&rwl); +} + diff --git a/unittest/mysys/my_vsnprintf-t.c b/unittest/mysys/my_vsnprintf-t.c new file mode 100644 index 00000000000..f3a6b5700da --- /dev/null +++ b/unittest/mysys/my_vsnprintf-t.c @@ -0,0 +1,155 @@ +/* Copyright (C) 2003 MySQL AB + + 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; version 2 of the License. + + 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 <my_global.h> +#include <m_string.h> +#include <tap.h> + +char buf[1024]; /* let's hope that's enough */ + +void test1(const char *res, const char *fmt, ...) +{ + va_list args; + size_t len; + va_start(args,fmt); + len= my_vsnprintf(buf, sizeof(buf)-1, fmt, args); + va_end(args); + ok(strlen(res) == len && strcmp(buf, res) == 0, "\"%s\"", buf); +} + +int main(void) +{ + plan(47); + + test1("Constant string", + "Constant string"); + + test1("Format specifier s works", + "Format specifier s %s", "works"); + test1("Format specifier b works (mysql extension)", + "Format specifier b %.5b (mysql extension)", "works!!!"); + test1("Format specifier c !", + "Format specifier c %c", '!'); + test1("Format specifier d 1", + "Format specifier d %d", 1); + test1("Format specifier u 2", + "Format specifier u %u", 2); + test1("Format specifier x a", + "Format specifier x %x", 10); + test1("Format specifier X B", + "Format specifier X %X", 11); + test1("Format specifier p 0x5", + "Format specifier p %p", 5); + + test1("Flag '-' is ignored < 1>", + "Flag '-' is ignored <%-4d>", 1); + test1("Flag '0' works <0006>", + "Flag '0' works <%04d>", 6); + + test1("Width is ignored for strings <x> <y>", + "Width is ignored for strings <%04s> <%5s>", "x", "y"); + + test1("Precision works for strings <abcde>", + "Precision works for strings <%.5s>", "abcdef!"); + + test1("Flag '`' (backtick) works: `abcd` `op``q` (mysql extension)", + "Flag '`' (backtick) works: %`s %`.4s (mysql extension)", + "abcd", "op`qrst"); + + test1("Length modifiers work: 1 * -1 * 2 * 3", + "Length modifiers work: %d * %ld * %lld * %zd", 1, -1L, 2LL, (size_t)3); + + test1("(null) pointer is fine", + "%s pointer is fine", NULL); + + test1("Positional arguments work: on the dark side they are", + "Positional arguments work: %3$s %1$s %2$s", + "they", "are", "on the dark side"); + + test1("Asterisk '*' as a width works: < 4>", + "Asterisk '*' as a width works: <%*d>", 5, 4); + + test1("Asterisk '*' as a precision works: <qwerty>", + "Asterisk '*' as a precision works: <%.*s>", 6, "qwertyuiop"); + + test1("Positional arguments for a width: < 4>", + "Positional arguments for a width: <%1$*2$d>", 4, 5); + + test1("Positional arguments for a precision: <qwerty>", + "Positional arguments for a precision: <%1$.*2$s>", "qwertyuiop", 6); + + test1("Positional arguments and a width: <0000ab>", + "Positional arguments and a width: <%1$06x>", 0xab); + + test1("Padding and %p <0x12> <0x034> <0x0000ab> < 0xcd>", + "Padding and %%p <%04p> <%05p> <%08p> <%8p>", 0x12, 0x34, 0xab, 0xcd); + +#if MYSQL_VERSION_ID > 60000 +#error %f/%g tests go here +#endif + + test1("Hello", + "Hello"); + test1("Hello int, 1", + "Hello int, %d", 1); + test1("Hello int, -1", + "Hello int, %d", -1); + test1("Hello string 'I am a string'", + "Hello string '%s'", "I am a string"); + test1("Hello hack hack hack hack hack hack hack 1", + "Hello hack hack hack hack hack hack hack %d", 1); + test1("Hello 1 hack 4", + "Hello %d hack %d", 1, 4); + test1("Hello 1 hack hack hack hack hack 4", + "Hello %d hack hack hack hack hack %d", 1, 4); + test1("Hello 'hack' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", + "Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", "hack"); + test1("Hello hhhhhhhhhhhhhh 1 sssssssssssssss", + "Hello hhhhhhhhhhhhhh %d sssssssssssssss", 1); + test1("Hello 1", + "Hello %u", 1); + test1("Hello 4294967295", + "Hello %u", -1); + test1("Hex: 20 ' 41'", + "Hex: %lx '%6lx'", 32, 65); + test1("conn 1 to: '(null)' user: '(null)' host: '(null)' ((null))", + "conn %ld to: '%-.64s' user: '%-.32s' host: '%-.64s' (%-.64s)", + 1L, NULL, NULL, NULL, NULL); + test1("Hello string `I am a string`", + "Hello string %`s", "I am a string"); + test1("Hello TEST", + "Hello %05s", "TEST"); + test1("My `Q` test", + "My %1$`-.1s test", "QQQQ"); + test1("My AAAA test done DDDD", + "My %2$s test done %1$s", "DDDD", "AAAA"); + test1("My DDDD test CCCC, DDD", + "My %1$s test %2$s, %1$-.3s", "DDDD", "CCCC"); + test1("My QQQQ test", + "My %1$`-.4b test", "QQQQ"); + test1("My X test", + "My %1$c test", 'X'); + test1("My <0000000010> test1 < a> test2 < A>", + "My <%010d> test1 <%4x> test2 <%4X>", 10, 10, 10); + test1("My <0000000010> test1 < a> test2 < a>", + "My <%1$010d> test1 <%2$4x> test2 <%2$4x>", 10, 10); + test1("My 00010 test", + "My %1$*02$d test", 10, 5); + test1("My `DDDD` test CCCC, `DDD`", + "My %1$`s test %2$s, %1$`-.3s", "DDDD", "CCCC"); + + return exit_status(); +} + diff --git a/unittest/mysys/thr_template.c b/unittest/mysys/thr_template.c new file mode 100644 index 00000000000..5fc0d1497bf --- /dev/null +++ b/unittest/mysys/thr_template.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc. + + 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; version 2 of the License. + + 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 <my_global.h> +#include <my_sys.h> +#include <my_atomic.h> +#include <tap.h> + +volatile uint32 bad; +pthread_attr_t thr_attr; +pthread_mutex_t mutex; +pthread_cond_t cond; +uint running_threads; + +void do_tests(); + +void test_concurrently(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t t; + ulonglong now= my_interval_timer(); + + bad= 0; + + diag("Testing %s with %d threads, %d iterations... ", test, n, m); + for (running_threads= n ; n ; n--) + { + if (pthread_create(&t, &thr_attr, handler, &m) != 0) + { + diag("Could not create thread"); + abort(); + } + } + pthread_mutex_lock(&mutex); + while (running_threads) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + + now= my_interval_timer() - now; + ok(!bad, "tested %s in %g secs (%d)", test, ((double)now)/1e9, bad); +} + +int main(int argc __attribute__((unused)), char **argv) +{ + MY_INIT("thd_template"); + + if (argv[1] && *argv[1]) + DBUG_SET_INITIAL(argv[1]); + + pthread_mutex_init(&mutex, 0); + pthread_cond_init(&cond, 0); + pthread_attr_init(&thr_attr); + pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); + +#ifdef MY_ATOMIC_MODE_RWLOCKS +#if defined(HPUX11) || defined(__POWERPC__) /* showed to be very slow (scheduler-related) */ +#define CYCLES 300 +#else +#define CYCLES 3000 +#endif +#else +#define CYCLES 3000 +#endif +#define THREADS 30 + + diag("N CPUs: %d, atomic ops: %s", my_getncpus(), MY_ATOMIC_MODE); + + do_tests(); + + /* + workaround until we know why it crashes randomly on some machine + (BUG#22320). + */ +#ifdef NOT_USED + sleep(1); +#endif + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&cond); + pthread_attr_destroy(&thr_attr); + my_end(0); + return exit_status(); +} + diff --git a/unittest/mysys/waiting_threads-t.c b/unittest/mysys/waiting_threads-t.c new file mode 100644 index 00000000000..35e86aca319 --- /dev/null +++ b/unittest/mysys/waiting_threads-t.c @@ -0,0 +1,291 @@ +/* Copyright (C) 2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + 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; version 2 of the License. + + 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 "thr_template.c" +#include <waiting_threads.h> +#include <m_string.h> + +struct test_wt_thd { + WT_THD thd; + pthread_mutex_t lock; +} thds[THREADS]; + +uint i, cnt; +pthread_mutex_t lock; +pthread_cond_t thread_sync; + +ulong wt_timeout_short=100, wt_deadlock_search_depth_short=4; +ulong wt_timeout_long=10000, wt_deadlock_search_depth_long=15; + +#define reset(ARRAY) bzero(ARRAY, sizeof(ARRAY)) + +/* see explanation of the kill strategies in waiting_threads.h */ +enum { LATEST, RANDOM, YOUNGEST, LOCKS } kill_strategy; + +WT_RESOURCE_TYPE restype={ wt_resource_id_memcmp, 0}; + +#define rnd() ((uint)(my_rnd(&rand) * INT_MAX32)) + +/* + stress test: wait on a random number of random threads. + it always succeeds (unless crashes or hangs). +*/ +pthread_handler_t test_wt(void *arg) +{ + int m, n, i, id, res; + struct my_rnd_struct rand; + + my_thread_init(); + + pthread_mutex_lock(&mutex); + id= cnt++; + wt_thd_lazy_init(& thds[id].thd, + & wt_deadlock_search_depth_short, & wt_timeout_short, + & wt_deadlock_search_depth_long, & wt_timeout_long); + + /* now, wait for everybody to be ready to run */ + if (cnt >= THREADS) + pthread_cond_broadcast(&thread_sync); + else + while (cnt < THREADS) + pthread_cond_wait(&thread_sync, &mutex); + pthread_mutex_unlock(&mutex); + + my_rnd_init(&rand, (ulong)(intptr)&m, id); + if (kill_strategy == YOUNGEST) + thds[id].thd.weight= (ulong) ~ my_interval_timer(); + if (kill_strategy == LOCKS) + thds[id].thd.weight= 0; + + for (m= *(int *)arg; m ; m--) + { + WT_RESOURCE_ID resid; + int blockers[THREADS/10], j, k; + + resid.value= id; + resid.type= &restype; + + res= 0; + + /* prepare for waiting for a random number of random threads */ + for (j= n= (rnd() % THREADS)/10; !res && j >= 0; j--) + { +retry: + i= rnd() % (THREADS-1); /* pick a random thread */ + if (i >= id) i++; /* with a number from 0 to THREADS-1 excluding ours */ + + for (k=n; k >=j; k--) /* the one we didn't pick before */ + if (blockers[k] == i) + goto retry; + blockers[j]= i; + + if (kill_strategy == RANDOM) + thds[id].thd.weight= rnd(); + + pthread_mutex_lock(& thds[i].lock); + res= wt_thd_will_wait_for(& thds[id].thd, & thds[i].thd, &resid); + pthread_mutex_unlock(& thds[i].lock); + } + + if (!res) + { + pthread_mutex_lock(&lock); + res= wt_thd_cond_timedwait(& thds[id].thd, &lock); + pthread_mutex_unlock(&lock); + } + + if (res) + { + pthread_mutex_lock(& thds[id].lock); + pthread_mutex_lock(&lock); + wt_thd_release_all(& thds[id].thd); + pthread_mutex_unlock(&lock); + pthread_mutex_unlock(& thds[id].lock); + if (kill_strategy == LOCKS) + thds[id].thd.weight= 0; + if (kill_strategy == YOUNGEST) + thds[id].thd.weight= (ulong)~ my_interval_timer(); + } + else if (kill_strategy == LOCKS) + thds[id].thd.weight++; + } + + pthread_mutex_lock(&mutex); + /* wait for everybody to finish */ + if (!--cnt) + pthread_cond_broadcast(&thread_sync); + else + while (cnt) + pthread_cond_wait(&thread_sync, &mutex); + + pthread_mutex_lock(& thds[id].lock); + pthread_mutex_lock(&lock); + wt_thd_release_all(& thds[id].thd); + pthread_mutex_unlock(&lock); + pthread_mutex_unlock(& thds[id].lock); + wt_thd_destroy(& thds[id].thd); + + if (!--running_threads) /* now, signal when everybody is done with deinit */ + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + DBUG_PRINT("wt", ("exiting")); + my_thread_end(); + return 0; +} + +void do_one_test() +{ + double sum, sum0; + DBUG_ENTER("do_one_test"); + + reset(wt_cycle_stats); + reset(wt_wait_stats); + wt_success_stats=0; + cnt=0; + test_concurrently("waiting_threads", test_wt, THREADS, CYCLES); + + sum=sum0=0; + for (cnt=0; cnt < WT_CYCLE_STATS; cnt++) + sum+= wt_cycle_stats[0][cnt] + wt_cycle_stats[1][cnt]; + for (cnt=0; cnt < WT_CYCLE_STATS; cnt++) + if (wt_cycle_stats[0][cnt] + wt_cycle_stats[1][cnt] > 0) + { + sum0+=wt_cycle_stats[0][cnt] + wt_cycle_stats[1][cnt]; + diag("deadlock cycles of length %2u: %4u %4u %8.2f %%", cnt, + wt_cycle_stats[0][cnt], wt_cycle_stats[1][cnt], 1e2*sum0/sum); + } + diag("depth exceeded: %u %u", + wt_cycle_stats[0][cnt], wt_cycle_stats[1][cnt]); + for (cnt=0; cnt < WT_WAIT_STATS; cnt++) + if (wt_wait_stats[cnt]>0) + diag("deadlock waits up to %7llu us: %5u", + wt_wait_table[cnt], wt_wait_stats[cnt]); + diag("timed out: %u", wt_wait_stats[cnt]); + diag("successes: %u", wt_success_stats); + + DBUG_VOID_RETURN; +} + +void do_tests() +{ + DBUG_ENTER("do_tests"); + if (skip_big_tests) + { + skip(1, "Big test skipped"); + return; + } + plan(14); + compile_time_assert(THREADS >= 4); + + DBUG_PRINT("wt", ("================= initialization ===================")); + + bad= my_atomic_initialize(); + ok(!bad, "my_atomic_initialize() returned %d", bad); + + pthread_cond_init(&thread_sync, 0); + pthread_mutex_init(&lock, 0); + wt_init(); + for (cnt=0; cnt < THREADS; cnt++) + pthread_mutex_init(& thds[cnt].lock, 0); + { + WT_RESOURCE_ID resid[4]; + for (i=0; i < array_elements(resid); i++) + { + wt_thd_lazy_init(& thds[i].thd, + & wt_deadlock_search_depth_short, & wt_timeout_short, + & wt_deadlock_search_depth_long, & wt_timeout_long); + resid[i].value= i+1; + resid[i].type= &restype; + } + + DBUG_PRINT("wt", ("================= manual test ===================")); + +#define ok_wait(X,Y, R) \ + ok(wt_thd_will_wait_for(& thds[X].thd, & thds[Y].thd, &resid[R]) == 0, \ + "thd[" #X "] will wait for thd[" #Y "]") +#define ok_deadlock(X,Y,R) \ + ok(wt_thd_will_wait_for(& thds[X].thd, & thds[Y].thd, &resid[R]) == WT_DEADLOCK, \ + "thd[" #X "] will wait for thd[" #Y "] - deadlock") + + ok_wait(0,1,0); + ok_wait(0,2,0); + ok_wait(0,3,0); + + pthread_mutex_lock(&lock); + bad= wt_thd_cond_timedwait(& thds[0].thd, &lock); + pthread_mutex_unlock(&lock); + ok(bad == WT_TIMEOUT, "timeout test returned %d", bad); + + ok_wait(0,1,0); + ok_wait(1,2,1); + ok_deadlock(2,0,2); + + pthread_mutex_lock(&lock); + ok(wt_thd_cond_timedwait(& thds[0].thd, &lock) == WT_TIMEOUT, "as always"); + ok(wt_thd_cond_timedwait(& thds[1].thd, &lock) == WT_TIMEOUT, "as always"); + wt_thd_release_all(& thds[0].thd); + wt_thd_release_all(& thds[1].thd); + wt_thd_release_all(& thds[2].thd); + wt_thd_release_all(& thds[3].thd); + + for (i=0; i < array_elements(resid); i++) + { + wt_thd_release_all(& thds[i].thd); + wt_thd_destroy(& thds[i].thd); + } + pthread_mutex_unlock(&lock); + } + + wt_deadlock_search_depth_short=6; + wt_timeout_short=1000; + wt_timeout_long= 100; + wt_deadlock_search_depth_long=16; + DBUG_PRINT("wt", ("================= stress test ===================")); + + diag("timeout_short=%lu us, deadlock_search_depth_short=%lu", + wt_timeout_short, wt_deadlock_search_depth_short); + diag("timeout_long=%lu us, deadlock_search_depth_long=%lu", + wt_timeout_long, wt_deadlock_search_depth_long); + +#ifndef _WIN32 +#define test_kill_strategy(X) \ + diag("kill strategy: " #X); \ + DBUG_EXECUTE("reset_file", \ + { rewind(DBUG_FILE); my_chsize(fileno(DBUG_FILE), 0, 0, MYF(MY_WME)); }); \ + DBUG_PRINT("info", ("kill strategy: " #X)); \ + kill_strategy=X; \ + do_one_test(); +#else +#define test_kill_strategy(X) \ + diag("kill strategy: " #X); \ + DBUG_PRINT("info", ("kill strategy: " #X)); \ + kill_strategy=X; \ + do_one_test(); +#endif + + test_kill_strategy(LATEST); + test_kill_strategy(RANDOM); + test_kill_strategy(YOUNGEST); + test_kill_strategy(LOCKS); + + DBUG_PRINT("wt", ("================= cleanup ===================")); + for (cnt=0; cnt < THREADS; cnt++) + pthread_mutex_destroy(& thds[cnt].lock); + wt_end(); + pthread_mutex_destroy(&lock); + pthread_cond_destroy(&thread_sync); + DBUG_VOID_RETURN; +} + diff --git a/unittest/mytap/CMakeLists.txt b/unittest/mytap/CMakeLists.txt new file mode 100644 index 00000000000..0579f075953 --- /dev/null +++ b/unittest/mytap/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2007 MySQL AB +# +# 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) +ADD_LIBRARY(mytap tap.c) diff --git a/unittest/mytap/Makefile.am b/unittest/mytap/Makefile.am index c02bcd3b49d..b14b65a6f5d 100644 --- a/unittest/mytap/Makefile.am +++ b/unittest/mytap/Makefile.am @@ -20,7 +20,6 @@ noinst_HEADERS = tap.h libmytap_a_SOURCES = tap.c -SUBDIRS = . t +EXTRA_DIST = CMakeLists.txt -# Don't update the files from bitkeeper -%::SCCS/s.% +SUBDIRS = . t diff --git a/unittest/mytap/t/Makefile.am b/unittest/mytap/t/Makefile.am index e89a088bb3a..bce72a88c05 100644 --- a/unittest/mytap/t/Makefile.am +++ b/unittest/mytap/t/Makefile.am @@ -20,6 +20,3 @@ AM_LDFLAGS = -L$(top_builddir)/unittest/mytap LDADD = -lmytap noinst_PROGRAMS = basic-t - -# Don't update the files from bitkeeper -%::SCCS/s.% diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c index 2566d90c4b0..e0e42c6eb5e 100644 --- a/unittest/mytap/tap.c +++ b/unittest/mytap/tap.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. + Copyright (c) 2011, Monty Program Ab 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 @@ -19,7 +20,7 @@ #include "tap.h" -#include "my_config.h" +#include "my_global.h" #include <stdlib.h> #include <stdarg.h> @@ -27,6 +28,20 @@ #include <string.h> #include <signal.h> +static ulong start_timer(void); +static void end_timer(ulong start_time,char *buff); +static void nice_time(double sec,char *buff,my_bool part_second); + +/* + Visual Studio 2003 does not know vsnprintf but knows _vsnprintf. + We don't put this #define in config-win.h because we prefer + my_vsnprintf everywhere instead, except when linking with libmysys + is not desirable - the case here. +*/ +#if defined(_MSC_VER) && ( _MSC_VER == 1310 ) +#define vsnprintf _vsnprintf +#endif + /** @defgroup MyTAP_Internal MyTAP Internals @@ -116,7 +131,7 @@ emit_endl() static void handle_core_signal(int signo) { - BAIL_OUT("Signal %d thrown", signo); + BAIL_OUT("Signal %d thrown\n", signo); } void @@ -126,6 +141,8 @@ BAIL_OUT(char const *fmt, ...) va_start(ap, fmt); fprintf(tapout, "Bail out! "); vfprintf(tapout, fmt, ap); + diag("%d tests planned, %d failed, %d was last executed", + g_test.plan, g_test.failed, g_test.last); emit_endl(); va_end(ap); exit(255); @@ -149,12 +166,15 @@ typedef struct signal_entry { } signal_entry; static signal_entry install_signal[]= { + { SIGINT, handle_core_signal }, { SIGQUIT, handle_core_signal }, { SIGILL, handle_core_signal }, { SIGABRT, handle_core_signal }, { SIGFPE, handle_core_signal }, - { SIGSEGV, handle_core_signal }, - { SIGBUS, handle_core_signal } + { SIGSEGV, handle_core_signal } +#ifdef SIGBUS + , { SIGBUS, handle_core_signal } +#endif #ifdef SIGXCPU , { SIGXCPU, handle_core_signal } #endif @@ -169,13 +189,25 @@ static signal_entry install_signal[]= { #endif }; +int skip_big_tests= 1; +ulong start_time= 0; + void -plan(int const count) +plan(int count) { + char *config= getenv("MYTAP_CONFIG"); + size_t i; + + start_time= start_timer(); + + if (config) + skip_big_tests= strcmp(config, "big"); + + setvbuf(tapout, 0, _IONBF, 0); /* provide output at once */ /* Install signal handler */ - size_t i; + for (i= 0; i < sizeof(install_signal)/sizeof(*install_signal); ++i) signal(install_signal[i].signo, install_signal[i].handler); @@ -208,7 +240,7 @@ skip_all(char const *reason, ...) } void -ok(int const pass, char const *fmt, ...) +ok(int pass, char const *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -265,6 +297,7 @@ skip(int how_many, char const *fmt, ...) } } + void todo_start(char const *message, ...) { @@ -280,7 +313,10 @@ todo_end() *g_test.todo = '\0'; } -int exit_status() { +int exit_status() +{ + char buff[60]; + /* If there were no plan, we write one last instead. */ @@ -299,10 +335,81 @@ int exit_status() { diag("Failed %d tests!", g_test.failed); return EXIT_FAILURE; } + if (start_time) + { + end_timer(start_time, buff); + printf("Test took %s\n", buff); + fflush(stdout); + } return EXIT_SUCCESS; } +#if defined(__WIN__) || defined(__NETWARE__) +#include <time.h> +#else +#include <sys/times.h> +#ifdef _SC_CLK_TCK // For mit-pthreads +#undef CLOCKS_PER_SEC +#define CLOCKS_PER_SEC (sysconf(_SC_CLK_TCK)) +#endif +#endif + +static ulong start_timer(void) +{ +#if defined(__WIN__) || defined(__NETWARE__) + return clock(); +#else + struct tms tms_tmp; + return times(&tms_tmp); +#endif +} + + +/** + Write as many as 52+1 bytes to buff, in the form of a legible + duration of time. + + len("4294967296 days, 23 hours, 59 minutes, 60.00 seconds") -> 52 +*/ + +static void nice_time(double sec,char *buff, my_bool part_second) +{ + ulong tmp; + if (sec >= 3600.0*24) + { + tmp=(ulong) floor(sec/(3600.0*24)); + sec-=3600.0*24*tmp; + buff+= my_sprintf(buff, (buff, "%ld %s", tmp, + tmp > 1 ? " days " : " day ")); + } + if (sec >= 3600.0) + { + tmp=(ulong) floor(sec/3600.0); + sec-=3600.0*tmp; + buff+= my_sprintf(buff, (buff, "%ld %s", tmp, + tmp > 1 ? " hours " : " hour ")); + } + if (sec >= 60.0) + { + tmp=(ulong) floor(sec/60.0); + sec-=60.0*tmp; + buff+= my_sprintf(buff, (buff, "%ld min ", tmp)); + } + if (part_second) + sprintf(buff,"%.2f sec",sec); + else + sprintf(buff,"%d sec",(int) sec); +} + + +static void end_timer(ulong start_time,char *buff) +{ + nice_time((double) (start_timer() - start_time) / + CLOCKS_PER_SEC,buff,1); +} + + /** @mainpage Testing C and C++ using MyTAP diff --git a/unittest/mytap/tap.h b/unittest/mytap/tap.h index 010121507bc..ee04f7dc049 100644 --- a/unittest/mytap/tap.h +++ b/unittest/mytap/tap.h @@ -62,6 +62,24 @@ extern "C" { #endif /** + Defines whether "big" tests should be skipped. + + This variable is set by plan() function unless MYTAP_CONFIG environment + variable is set to the string "big". It is supposed to be used as + + @code + if (skip_big_tests) { + skip(1, "Big test skipped"); + } else { + ok(life_universe_and_everything() == 42, "The answer is CORRECT"); + } + @endcode + + @see SKIP_BIG_TESTS +*/ +extern int skip_big_tests; + +/** @defgroup MyTAP_API MyTAP API MySQL support for performing unit tests according to TAP. @@ -80,10 +98,15 @@ extern "C" { that generate a core, so if you want to override these signals, do it <em>after</em> you have called the plan() function. - @param count The planned number of tests to run. + It will also set skip_big_tests variable if MYTAP_CONFIG environment + variable is defined. + + @see skip_big_tests + + @param count The planned number of tests to run. */ -void plan(int const count); +void plan(int count); /** @@ -102,7 +125,7 @@ void plan(int const count); use ok1() in this case. */ -void ok(int const pass, char const *fmt, ...) +void ok(int pass, char const *fmt, ...) __attribute__((format(printf,2,3))); @@ -171,6 +194,24 @@ void skip(int how_many, char const *reason, ...) /** + Helper macro to skip a group of "big" tests. It is used in the following + manner: + + @code + SKIP_BIG_TESTS(1) + { + ok(life_universe_and_everything() == 42, "The answer is CORRECT"); + } + @endcode + + @see skip_big_tests + */ + +#define SKIP_BIG_TESTS(COUNT) \ + if (skip_big_tests) skip((COUNT), "big test"); else + + +/** Print a diagnostics message. @param fmt Diagnostics message in printf() format. diff --git a/unittest/strings/Makefile.am b/unittest/strings/Makefile.am index 5b18d89f58e..3e5a0f3e9cb 100644 --- a/unittest/strings/Makefile.am +++ b/unittest/strings/Makefile.am @@ -22,6 +22,3 @@ LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ $(top_builddir)/strings/libmystrings.a noinst_PROGRAMS = strings-t - -# Don't update the files from bitkeeper -%::SCCS/s.% diff --git a/unittest/strings/strings-t.c b/unittest/strings/strings-t.c index 278f42e56a5..cb33af3ac03 100644 --- a/unittest/strings/strings-t.c +++ b/unittest/strings/strings-t.c @@ -87,7 +87,9 @@ static CHARSET_INFO *charset_list[]= #endif #ifdef HAVE_CHARSET_utf8 &my_charset_utf8_general_ci, +#ifdef HAVE_HAVE_UCA_COLLATIONS &my_charset_utf8_unicode_ci, +#endif &my_charset_utf8_bin, #endif }; diff --git a/unittest/unit.pl b/unittest/unit.pl index 9d328985012..4eaf4cd2f9b 100644 --- a/unittest/unit.pl +++ b/unittest/unit.pl @@ -14,8 +14,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -use Test::Harness qw(&runtests $verbose); +use Test::Harness; use File::Find; +use Getopt::Long; use strict; @@ -31,10 +32,19 @@ unit - Run unit tests in directory =head1 SYNOPSIS - unit run + unit [--[no]big] [--[no]verbose] run [tests to run] =cut +my $big= $ENV{'MYTAP_CONFIG'} eq 'big'; + +my $result = GetOptions ( + "big!" => \$big, + "verbose!" => \$Test::Harness::verbose, +); + +$ENV{'MYTAP_CONFIG'} = $big ? 'big' : ''; + my $cmd = shift; if (defined $cmd && exists $dispatch{$cmd}) { @@ -55,8 +65,8 @@ sub _find_test_files (@) { my @dirs = @_; my @files; find sub { - $File::Find::prune = 1 if /^SCCS$/; - push(@files, $File::Find::name) if -x _ && /-t\z/; + $File::Find::prune = 1 if /^(SCCS|\.libs)$/; + push(@files, $File::Find::name) if -x _ && (/-t\z/ || /-t\.exe\z/); }, @dirs; return @files; } @@ -92,8 +102,9 @@ sub run_cmd (@) { if (@files > 0) { # Removing the first './' from the file names foreach (@files) { s!^\./!! } - $ENV{'HARNESS_PERL_SWITCHES'} .= q" -e 'exec @ARGV'"; + $ENV{'HARNESS_PERL_SWITCHES'} .= ' -e "exec @ARGV"'; + $ENV{'HARNESS_OPTIONS'}="j4"; + $Test::Harness::Timer = 1; runtests @files; } } - |