diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:04:16 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:04:16 +0000 |
commit | a68fb2d8219f6bccc573009600e9f23e89226a5e (patch) | |
tree | d742d35d14ae816e99293d2b01face30e9f3a46b /unittest | |
parent | Initial commit. (diff) | |
download | mariadb-10.6-upstream.tar.xz mariadb-10.6-upstream.zip |
Adding upstream version 1:10.6.11.upstream/1%10.6.11upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'unittest')
48 files changed, 9585 insertions, 0 deletions
diff --git a/unittest/README.txt b/unittest/README.txt new file mode 100644 index 00000000..ea94a505 --- /dev/null +++ b/unittest/README.txt @@ -0,0 +1,53 @@ + +Unit tests directory structure +------------------------------ + +This is the current structure of the unit tests. More directories +will be added over time. + +mytap Source for the MyTAP library +mysys Tests for mysys components + bitmap-t.c Unit test for MY_BITMAP + base64-t.c Unit test for base64 encoding functions +examples Example unit tests. + core-t.c Example of raising a signal in the middle of the test + THIS TEST WILL STOP ALL FURTHER TESTING! + simple-t.c Example of a standard TAP unit test + skip-t.c Example where some test points are skipped + skip_all-t.c Example of a test where the entire test is skipped + todo-t.c Example where test contain test points that are TODO + no_plan-t.c Example of a test with no plan (avoid this) + + +Executing unit tests +-------------------- + +To make and execute all unit tests in the directory: + + make test + +Observe that the tests in the examples/ directory are just various +examples of tests and are not expected to pass. + + +Adding unit tests +----------------- + +Add a file with a name of the format "foo-t.c" to the appropriate +directory and add the following to the Makefile.am in that directory +(where ... denotes stuff already there): + + noinst_PROGRAMS = ... foo-t + +Note, it's important to have "-t" at the end of the filename, otherwise the +test won't be executed by 'make test' ! + + +Documentation +------------- + +The generated documentation is temporarily placed at: + + http://www.kindahl.net/mytap/doc/ + +I will move it to a better place once I figure out where and how. diff --git a/unittest/embedded/CMakeLists.txt b/unittest/embedded/CMakeLists.txt new file mode 100644 index 00000000..cf48550c --- /dev/null +++ b/unittest/embedded/CMakeLists.txt @@ -0,0 +1,20 @@ + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/libmysqld/include + ${PCRE_INCLUDES} + ${CMAKE_SOURCE_DIR}/sql + ${MY_READLINE_INCLUDE_DIR} + ) + + +ADD_DEFINITIONS(-DEMBEDDED_LIBRARY -UMYSQL_CLIENT) + + +MYSQL_ADD_EXECUTABLE(test-connect-t test-connect.cc + COMPONENT Test) +TARGET_LINK_LIBRARIES(test-connect-t mysqlserver ) +MY_ADD_TEST(test-connect) + +IF(UNIX) +SET_TARGET_PROPERTIES(test-connect-t PROPERTIES ENABLE_EXPORTS TRUE) +ENDIF() diff --git a/unittest/embedded/test-connect.cc b/unittest/embedded/test-connect.cc new file mode 100644 index 00000000..cd122286 --- /dev/null +++ b/unittest/embedded/test-connect.cc @@ -0,0 +1,78 @@ +#include <mysql.h> +#include <stdio.h> +#include <stdlib.h> + +int get_evar(char **hostname, char **port, char** username, char ** password) +{ + + if (!((*hostname)= getenv("MYSQL_TEST_HOST"))) + (*hostname)= (char *)"127.0.0.1"; + + if (!((*port)= getenv("MASTER_MYPORT"))) + { + if (!((*port)= getenv("MYSQL_TEST_PORT"))) + return 1; + } + + if (!((*username)= getenv("MYSQL_TEST_USER"))) + (*username)= (char *)"root"; + + if (!((*password)= getenv("MYSQL_TEST_PASSWD"))) + (*password)= (char *)""; + + return 0; +} + +int main(int argc, char *argv[]) +{ + MYSQL *mysql; + char *host; + char *user; + char *passwd; + char *porta; + unsigned int port; + + if (get_evar(&host, &porta, &user, &passwd)) + { + printf("set environment variable MASTER_MYPORT\n"); + return 1; + } + + port = atoi(porta); + + mysql_thread_init(); + + if (mysql_server_init(-1, NULL, NULL) != 0) { + printf("mysql_library_init failed"); + return 1; + } + + + mysql = mysql_init(NULL); + + if (!mysql) { + printf("mysql_init failed"); + return 1; + } + + if (mysql_options(mysql, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL) != 0) { + printf("mysql_options MYSQL_OPT_USE_REMOTE_CONNECTION failed: %s\n", mysql_error(mysql)); + return 1; + } + + if (mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4") != 0) { + printf("mysql_options MYSQL_SET_CHARSET_NAME utf8mb4 failed: %s\n", mysql_error(mysql)); + return 1; + } + + if (!mysql_real_connect(mysql, host, user, passwd, NULL, port, NULL, CLIENT_FOUND_ROWS | CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS)) { + printf("mysql_real_connect failed: %s\n", mysql_error(mysql)); + return 1; + } + mysql_close(mysql); + mysql_thread_end(); + mysql_library_end(); + + return 0; + +} diff --git a/unittest/examples/CMakeLists.txt b/unittest/examples/CMakeLists.txt new file mode 100644 index 00000000..cba7db5b --- /dev/null +++ b/unittest/examples/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2006, 2010, 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 +# 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-1335 USA + +MY_ADD_TESTS(simple skip todo skip_all no_plan) diff --git a/unittest/examples/core-t.c b/unittest/examples/core-t.c new file mode 100644 index 00000000..12d9fe5c --- /dev/null +++ b/unittest/examples/core-t.c @@ -0,0 +1,34 @@ +/* Copyright (c) 2006 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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-1335 USA */ + +#include <my_global.h> + +#include <stdlib.h> +#include <tap.h> + +/* + This is a simple test to demonstrate what happens if a signal that + generates a core is raised. + + Note that this test will stop all further testing! + */ + +int main() { + plan(3); + ok(1, "First test"); + abort(); + return exit_status(); +} diff --git a/unittest/examples/no_plan-t.c b/unittest/examples/no_plan-t.c new file mode 100644 index 00000000..80a56970 --- /dev/null +++ b/unittest/examples/no_plan-t.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2006, 2010, Oracle and/or its affiliates + + 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-1335 USA */ + +#include <my_global.h> + +#include <stdlib.h> +#include <tap.h> + +/* + Sometimes, the number of tests is not known beforehand. In those + cases, you should invoke plan(NO_PLAN). + The plan will be printed at the end of the test (inside exit_status()). + + Use this sparingly, it is a last resort: planning how many tests you + are going to run will help you catch that offending case when some + tests are skipped for an unknown reason. +*/ +int main() { + /* + We recommend calling plan(NO_PLAN), but want to verify that + omitting the call works as well. + plan(NO_PLAN); + */ + ok(1, " "); + ok(1, " "); + ok(1, " "); + return exit_status(); +} diff --git a/unittest/examples/simple-t.c b/unittest/examples/simple-t.c new file mode 100644 index 00000000..ea64696c --- /dev/null +++ b/unittest/examples/simple-t.c @@ -0,0 +1,53 @@ +/* Copyright (C) 2006 MySQL AB + Use is subject to license terms + + 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-1335 USA */ + +#include <tap.h> + +unsigned int gcs(unsigned int a, unsigned int b) +{ + if (b > a) { + unsigned int t = a; + a = b; + b = t; + } + + while (b != 0) { + unsigned int m = a % b; + a = b; + b = m; + } + return a; +} + +int main() { + unsigned int a,b; + unsigned int failed; + plan(1); + diag("Testing basic functions"); + failed = 0; + for (a = 1 ; a < 2000 ; ++a) + for (b = 1 ; b < 2000 ; ++b) + { + unsigned int d = gcs(a, b); + if (a % d != 0 || b % d != 0) { + ++failed; + diag("Failed for gcs(%4u,%4u)", a, b); + } + } + ok(failed == 0, "Testing gcs()"); + return exit_status(); +} + diff --git a/unittest/examples/skip-t.c b/unittest/examples/skip-t.c new file mode 100644 index 00000000..a8ca43f6 --- /dev/null +++ b/unittest/examples/skip-t.c @@ -0,0 +1,29 @@ +/* + Copyright (c) 2006, 2010, Oracle and/or its affiliates + + 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-1335 USA */ + +#include <tap.h> +#include <stdlib.h> + +int main() { + plan(4); + ok1(1); + ok1(1); + SKIP_BLOCK_IF(1, 2, "Example of skipping a few test points in a test") { + ok1(1); + ok1(1); + } + return exit_status(); +} diff --git a/unittest/examples/skip_all-t.c b/unittest/examples/skip_all-t.c new file mode 100644 index 00000000..cfe6432b --- /dev/null +++ b/unittest/examples/skip_all-t.c @@ -0,0 +1,40 @@ +/* + Copyright (c) 2006, 2010, Oracle and/or its affiliates + + 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-1335 USA */ + +#include <my_global.h> + +#include <stdlib.h> +#include <tap.h> + +int has_feature() { + return 0; +} + +/* + In some cases, an entire test file does not make sense because there + some feature is missing. In that case, the entire test case can be + skipped in the following manner. + */ +int main() { + if (!has_feature()) + skip_all("Example of skipping an entire test"); + plan(4); + ok1(1); + ok1(1); + ok1(1); + ok1(1); + return exit_status(); +} diff --git a/unittest/examples/todo-t.c b/unittest/examples/todo-t.c new file mode 100644 index 00000000..91720b30 --- /dev/null +++ b/unittest/examples/todo-t.c @@ -0,0 +1,36 @@ +/* + Copyright (c) 2006, 2010, Oracle and/or its affiliates + + 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-1335 USA */ + +#include <my_global.h> + +#include <stdlib.h> +#include <tap.h> + +int main() +{ + plan(4); + ok1(1); + ok1(1); + /* + Tests in the todo region is expected to fail. If they don't, + something is strange. + */ + todo_start("Need to fix these"); + ok1(0); + ok1(0); + todo_end(); + return exit_status(); +} diff --git a/unittest/json_lib/CMakeLists.txt b/unittest/json_lib/CMakeLists.txt new file mode 100644 index 00000000..1b2a89b2 --- /dev/null +++ b/unittest/json_lib/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2006, 2013, 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 +# 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-1335 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/unittest/mytap) + +# +MY_ADD_TESTS(json_lib LINK_LIBRARIES strings dbug) diff --git a/unittest/json_lib/json_lib-t.c b/unittest/json_lib/json_lib-t.c new file mode 100644 index 00000000..378ebe20 --- /dev/null +++ b/unittest/json_lib/json_lib-t.c @@ -0,0 +1,186 @@ +/* Copyright (c) 2016, MariaDB Corp. 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 + 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 "my_config.h" +#include "config.h" +#include <tap.h> +#include <my_global.h> +#include <my_sys.h> +#include <json_lib.h> + +/* The character set used for JSON all over this test. */ +static CHARSET_INFO *ci; + +#define s_e(j) j, j + strlen((const char *) j) + + +struct st_parse_result +{ + int n_keys; + int n_values; + int n_arrays; + int n_objects; + int n_steps; + int error; + uchar keyname_csum; +}; + + +static void parse_json(const uchar *j, struct st_parse_result *result) +{ + json_engine_t je; + + bzero(result, sizeof(*result)); + + if (json_scan_start(&je, ci, s_e(j))) + return; + + do + { + result->n_steps++; + switch (je.state) + { + case JST_KEY: + result->n_keys++; + while (json_read_keyname_chr(&je) == 0) + { + result->keyname_csum^= je.s.c_next; + } + if (je.s.error) + return; + break; + case JST_VALUE: + result->n_values++; + break; + case JST_OBJ_START: + result->n_objects++; + break; + case JST_ARRAY_START: + result->n_arrays++; + break; + default: + break; + }; + } while (json_scan_next(&je) == 0); + + result->error= je.s.error; +} + + +static const uchar *js0= (const uchar *) "123"; +static const uchar *js1= (const uchar *) "[123, \"text\"]"; +static const uchar *js2= (const uchar *) "{\"key1\":123, \"key2\":\"text\"}"; +static const uchar *js3= (const uchar *) "{\"key1\":{\"ikey1\":321}," + "\"key2\":[\"text\", 321]}"; + +/* + Test json_lib functions to parse JSON. +*/ +static void +test_json_parsing() +{ + struct st_parse_result r; + parse_json(js0, &r); + ok(r.n_steps == 1 && r.n_values == 1, "simple value"); + parse_json(js1, &r); + ok(r.n_steps == 5 && r.n_values == 3 && r.n_arrays == 1, "array"); + parse_json(js2, &r); + ok(r.n_steps == 5 && r.n_keys == 2 && r.n_objects == 1 && r.keyname_csum == 3, + "object"); + parse_json(js3, &r); + ok(r.n_steps == 12 && r.n_keys == 3 && r.n_objects == 2 && + r.n_arrays == 1 && r.keyname_csum == 44, + "complex json"); +} + + +static const uchar *p0= (const uchar *) "$.key1[12].*[*]"; +/* + Test json_lib functions to parse JSON path. +*/ +static void +test_path_parsing() +{ + json_path_t p; + if (json_path_setup(&p, ci, s_e(p0))) + return; + ok(p.last_step - p.steps == 4 && + p.steps[0].type == JSON_PATH_ARRAY_WILD && + p.steps[1].type == JSON_PATH_KEY && + p.steps[2].type == JSON_PATH_ARRAY && p.steps[2].n_item == 12 && + p.steps[3].type == JSON_PATH_KEY_WILD && + p.steps[4].type == JSON_PATH_ARRAY_WILD, + "path"); +} + + +static const uchar *fj0=(const uchar *) "[{\"k0\":123, \"k1\":123, \"k1\":123}," + " {\"k3\":321, \"k4\":\"text\"}," + " {\"k1\":[\"text\"], \"k2\":123}]"; +static const uchar *fp0= (const uchar *) "$[*].k1"; +/* + Test json_lib functions to search through JSON. +*/ +static void +test_search() +{ + json_engine_t je; + json_path_t p; + json_path_step_t *cur_step; + int n_matches, scal_values; + uint array_counters[JSON_DEPTH_LIMIT]; + + if (json_scan_start(&je, ci, s_e(fj0)) || + json_path_setup(&p, ci, s_e(fp0))) + return; + + cur_step= p.steps; + n_matches= scal_values= 0; + while (json_find_path(&je, &p, &cur_step, array_counters) == 0) + { + n_matches++; + if (json_read_value(&je)) + return; + if (json_value_scalar(&je)) + { + scal_values++; + if (json_scan_next(&je)) + return; + } + else + { + if (json_skip_level(&je) || json_scan_next(&je)) + return; + } + + } + + ok(n_matches == 3, "search"); +} + + +int main() +{ + ci= &my_charset_utf8mb3_general_ci; + + plan(6); + diag("Testing json_lib functions."); + + test_json_parsing(); + test_path_parsing(); + test_search(); + + return exit_status(); +} diff --git a/unittest/my_decimal/CMakeLists.txt b/unittest/my_decimal/CMakeLists.txt new file mode 100644 index 00000000..0b5b2282 --- /dev/null +++ b/unittest/my_decimal/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2006, 2013, 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 +# 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-1335 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/unittest/mytap) + +# +MY_ADD_TESTS(my_decimal EXT "cc" LINK_LIBRARIES strings dbug) diff --git a/unittest/my_decimal/my_decimal-t.cc b/unittest/my_decimal/my_decimal-t.cc new file mode 100644 index 00000000..b14c6794 --- /dev/null +++ b/unittest/my_decimal/my_decimal-t.cc @@ -0,0 +1,102 @@ +/* Copyright (c) 2013, 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 + 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-1335 USA */ + +#include "my_config.h" +#include "config.h" +#include <tap.h> +#include <my_global.h> +#include <my_sys.h> +#include <m_string.h> +#include <sql_string.h> +#include <my_decimal.h> + + + +/* + Test my_decimal constuctor and assignement operators +*/ +static int +test_copy_and_compare() +{ + my_decimal d1,d2; + + ulonglong val= 42; + + ok(ulonglong2decimal(val,&d1) == 0, "Pass"); + d2= d1; + my_decimal d3(d1); + + ok(my_decimal_cmp(&d1, &d2) == 0, "Pass"); + ok(my_decimal_cmp(&d2, &d3) == 0, "Pass"); + ok(my_decimal_cmp(&d3, &d1) == 0,"Pass"); + + ulonglong val1, val2, val3; + ok(decimal2ulonglong(&d1, &val1) == 0, "Pass"); + ok(decimal2ulonglong(&d2, &val2) == 0,"Pass"); + ok(decimal2ulonglong(&d3, &val3) == 0,"Pass"); + + ok(val == val1,"Pass"); + ok(val == val2,"Pass"); + ok(val == val3,"Pass"); + + // The CTOR/operator=() generated by the compiler would fail here: + val= 45; + ok(ulonglong2decimal(val, &d1) == 0,"Pass"); + ok(my_decimal_cmp(&d1, &d2) == 1,"Pass"); + ok(my_decimal_cmp(&d1, &d3) == 1,"Pass"); + + return 0; + +} + +static int +test_decimal2string() +{ + decimal_t d1; + decimal_digit_t buffer[DECIMAL_BUFF_LENGTH+2]; + char *str_end; + const char strnum[]= "0.1234567890123456789012345678901234567890123467"; + char strbuff[50]; + int len= 40; + int i; + + bzero(strbuff, sizeof(strbuff)); + str_end= (char *)(strnum + (sizeof(strnum) - 1)); + + d1.len= DECIMAL_BUFF_LENGTH + 2; + d1.buf= buffer; + + string2decimal(strnum, &d1, &str_end); + decimal2string(&d1, strbuff, &len, 0, 0, 'X'); + + /* last digit is not checked due to possible rounding */ + for (i= 0; i < 38 && strbuff[i] == strnum[i]; i++); + ok(i == 38, "Number"); + for (i= 39; i < 50 && strbuff[i] == 0; i++); + ok(i == 50, "No overrun"); + + return 0; + +} +int main() +{ + plan(15); + diag("Testing my_decimal constructor and assignment operators"); + + test_copy_and_compare(); + test_decimal2string(); + + return exit_status(); +} diff --git a/unittest/mysys/CMakeLists.txt b/unittest/mysys/CMakeLists.txt new file mode 100644 index 00000000..4b947ab7 --- /dev/null +++ b/unittest/mysys/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2006, 2013, 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 +# 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-1335 USA + +MY_ADD_TESTS(bitmap base64 my_atomic my_rdtsc lf my_malloc my_getopt dynstring + byte_order + queues stacktrace crc32 LINK_LIBRARIES mysys) +MY_ADD_TESTS(my_vsnprintf LINK_LIBRARIES strings mysys) +MY_ADD_TESTS(aes LINK_LIBRARIES mysys mysys_ssl) +ADD_DEFINITIONS(${SSL_DEFINES}) +INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR}) +MY_ADD_TESTS(ma_dyncol LINK_LIBRARIES mysys) + +IF(WIN32) + MY_ADD_TESTS(my_delete LINK_LIBRARIES mysys) +ENDIF() + diff --git a/unittest/mysys/aes-t.c b/unittest/mysys/aes-t.c new file mode 100644 index 00000000..cbec2760 --- /dev/null +++ b/unittest/mysys/aes-t.c @@ -0,0 +1,158 @@ +/* Copyright (c) 2003, 2006, 2007 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <my_crypt.h> +#include <tap.h> +#include <string.h> +#include <ctype.h> + + +/** Test streaming encryption, bytewise update.*/ +static int aes_crypt_bytewise(enum my_aes_mode mode, int flags, const unsigned char *src, + unsigned int slen, unsigned char *dst, unsigned int *dlen, + const unsigned char *key, unsigned int klen, + const unsigned char *iv, unsigned int ivlen) +{ + /* Allocate context on odd address on stack, in order to + catch misalignment errors.*/ + void *ctx= (char *)alloca(MY_AES_CTX_SIZE+1)+1; + + int res1, res2; + uint d1= 0, d2; + uint i; + + if ((res1= my_aes_crypt_init(ctx, mode, flags, key, klen, iv, ivlen))) + return res1; + for (i= 0; i < slen; i++) + { + uint tmp_d1=0; + res1= my_aes_crypt_update(ctx, src+i,1, dst, &tmp_d1); + if (res1) + return res1; + d1+= tmp_d1; + dst+= tmp_d1; + } + res2= my_aes_crypt_finish(ctx, dst, &d2); + *dlen= d1 + d2; + return res1 ? res1 : res2; +} + + +#ifndef HAVE_EncryptAes128Ctr +const uint MY_AES_CTR=0xDEADBEAF; +#endif +#ifndef HAVE_EncryptAes128Gcm +const uint MY_AES_GCM=0xDEADBEAF; +#endif + +#define MY_AES_UNSUPPORTED(x) (x == 0xDEADBEAF) + +static void do_test(uint mode, const char *mode_str, int nopad, uint slen, + char fill, size_t dlen, const char *hash) +{ + uchar key[16]= {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}; + uchar iv[16]= {2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7}; + uchar src[1000], dst[1100], dst2[1100], ddst[1000]; + uchar md5[MY_MD5_HASH_SIZE]; + uint src_len, dst_len, dst_len2, ddst_len; + int result; + + if (MY_AES_UNSUPPORTED(mode)) + { + skip(nopad?7:6, "%s not supported", mode_str); + return; + } + memset(src, fill, src_len= slen); + result= my_aes_crypt(mode, nopad | ENCRYPTION_FLAG_ENCRYPT, src, src_len, + dst, &dst_len, key, sizeof(key), iv, sizeof(iv)); + ok(result == MY_AES_OK, "encrypt %s %u %s", mode_str, src_len, + nopad ? "nopad" : "pad"); + + if (nopad) + { + result= aes_crypt_bytewise(mode, nopad | ENCRYPTION_FLAG_ENCRYPT, src, + src_len, dst2, &dst_len2, key, sizeof(key), + iv, sizeof(iv)); + ok(result == MY_AES_OK, "encrypt bytewise %s %u", mode_str, src_len); + /* Compare with non-bytewise encryption result*/ + ok(dst_len == dst_len2 && memcmp(dst, dst2, dst_len) == 0, + "memcmp bytewise %s %u", mode_str, src_len); + } + else + { + int dst_len_real= my_aes_get_size(mode, src_len); + ok(dst_len_real= dst_len, "my_aes_get_size"); + } + my_md5(md5, (char *) dst, dst_len); + ok(dst_len == dlen, "md5 len"); + ok(memcmp(md5, hash, sizeof(md5)) == 0, "md5"); + result= my_aes_crypt(mode, nopad | ENCRYPTION_FLAG_DECRYPT, + dst, dst_len, ddst, &ddst_len, key, sizeof(key), iv, + sizeof(iv)); + + ok(result == MY_AES_OK, "decrypt %s %u", mode_str, dst_len); + ok(ddst_len == src_len && memcmp(src, ddst, src_len) == 0, "memcmp"); +} + +#define DO_TEST_P(M, S, F, D, H) do_test(M, #M, 0, S, F, D, H) +#define DO_TEST_N(M, S, F, D, H) do_test(M, #M, ENCRYPTION_FLAG_NOPAD, S, F, D, H) + +/* useful macro for debugging */ +#define PRINT_MD5() \ + do { \ + uint i; \ + printf("\""); \ + for (i=0; i < sizeof(md5); i++) \ + printf("\\x%02x", md5[i]); \ + printf("\"\n"); \ + } while(0); + + +int +main(int argc __attribute__((unused)),char *argv[]) +{ + + MY_INIT(argv[0]); + + plan(122); + + DO_TEST_P(MY_AES_ECB, 200, '.', 208, "\xd8\x73\x8e\x3a\xbc\x66\x99\x13\x7f\x90\x23\x52\xee\x97\x6f\x9a"); + DO_TEST_P(MY_AES_ECB, 128, '?', 144, "\x19\x58\x33\x85\x4c\xaa\x7f\x06\xd1\xb2\xec\xd7\xb7\x6a\xa9\x5b"); + DO_TEST_P(MY_AES_CBC, 159, '%', 160, "\x4b\x03\x18\x3d\xf1\xa7\xcd\xa1\x46\xb3\xc6\x8a\x92\xc0\x0f\xc9"); + DO_TEST_P(MY_AES_CBC, 192, '@', 208, "\x54\xc4\x75\x1d\xff\xe0\xf6\x80\xf0\x85\xbb\x8b\xda\x07\x21\x17"); + DO_TEST_N(MY_AES_ECB, 200, '.', 200, "\xbf\xec\x43\xd1\x66\x8d\x01\xad\x3a\x25\xee\xa6\x3d\xc6\xc4\x68"); + DO_TEST_N(MY_AES_ECB, 128, '?', 128, "\x5b\x44\x20\xf3\xd9\xb4\x9d\x74\x5e\xb7\x5a\x0a\xe7\x32\x35\xc3"); + DO_TEST_N(MY_AES_CBC, 159, '%', 159, "\xf3\x6e\x40\x00\x3c\x08\xa0\xb1\x2d\x1f\xcf\xce\x54\xc9\x73\x83"); + DO_TEST_N(MY_AES_CBC, 192, '@', 192, "\x30\xe5\x28\x8c\x4a\x3b\x02\xd7\x56\x40\x59\x25\xac\x58\x09\x22"); + DO_TEST_P(MY_AES_CTR, 200, '.', 200, "\x5a\x77\x19\xea\x67\x50\xe3\xab\x7f\x39\x6f\xc4\xa8\x09\xc5\x88"); + DO_TEST_P(MY_AES_GCM, 128, '?', 144, "\x54\x6a\x7c\xa2\x04\xdc\x6e\x80\x1c\xcd\x5f\x7a\x7b\x08\x9e\x9d"); + + /* test short inputs (less that one block) */ + DO_TEST_P(MY_AES_ECB, 1, '.', 16, "\x6c\xd7\x66\x5b\x1b\x1e\x3a\x04\xfd\xb1\x91\x8d\x0e\xfd\xf1\x86"); + DO_TEST_P(MY_AES_ECB, 2, '?', 16, "\xdb\x84\x9e\xaf\x5f\xcc\xdb\x6b\xf2\x1c\xeb\x53\x75\xa3\x53\x5e"); + DO_TEST_P(MY_AES_CBC, 3, '%', 16, "\x60\x8e\x45\x9a\x07\x39\x63\xce\x02\x19\xdd\x52\xe3\x09\x2a\x66"); + DO_TEST_P(MY_AES_CBC, 4, '@', 16, "\x90\xc2\x6b\xf8\x84\x79\x83\xbd\xc1\x60\x71\x04\x55\x6a\xce\x9e"); + DO_TEST_N(MY_AES_ECB, 5, '.', 5, "\x6b\x60\xdc\xa4\x24\x9b\x02\xbb\x24\x41\x9b\xb0\xd1\x01\xcd\xba"); + DO_TEST_N(MY_AES_ECB, 6, '?', 6, "\x35\x8f\xb7\x9d\xd9\x61\x21\xcf\x25\x66\xd5\x9e\x91\xc1\x42\x7e"); + DO_TEST_N(MY_AES_CBC, 7, '%', 7, "\x94\x5e\x80\x71\x41\x7a\x64\x5d\x6f\x2e\x5b\x66\x9b\x5a\x3d\xda"); + DO_TEST_N(MY_AES_CBC, 8, '@', 8, "\xb8\x53\x97\xb9\x40\xa6\x98\xaf\x0c\x7b\x9a\xac\xad\x7e\x3c\xe0"); + DO_TEST_P(MY_AES_GCM, 9, '?', 25, "\x5e\x05\xfd\xb2\x8e\x17\x04\x1e\xff\x6d\x71\x81\xcd\x85\x8d\xb5"); + + my_end(0); + return exit_status(); +} diff --git a/unittest/mysys/base64-t.c b/unittest/mysys/base64-t.c new file mode 100644 index 00000000..590e285d --- /dev/null +++ b/unittest/mysys/base64-t.c @@ -0,0 +1,99 @@ +/* Copyright (c) 2003, 2006, 2007 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <tap.h> +#include <string.h> + +#define BASE64_LOOP_COUNT 500 +#define BASE64_ROWS 4 /* Number of ok(..) */ + +int +main(int argc __attribute__((unused)),char *argv[]) +{ + int i, cmp; + size_t j, k, l, dst_len, needed_length; + MY_INIT(argv[0]); + + plan(BASE64_LOOP_COUNT * BASE64_ROWS); + + for (i= 0; i < BASE64_LOOP_COUNT; i++) + { + /* Create source data */ + const size_t src_len= rand() % 1000 + 1; + + char * src= (char *) malloc(src_len); + char * s= src; + char * str; + char * dst; + + for (j= 0; j<src_len; j++) + { + char c= rand(); + *s++= c; + } + + /* Encode */ + needed_length= my_base64_needed_encoded_length((int)src_len); + str= (char *) malloc(needed_length); + for (k= 0; k < needed_length; k++) + str[k]= 0xff; /* Fill memory to check correct NUL termination */ + ok(my_base64_encode(src, src_len, str) == 0, + "my_base64_encode: size %d", i); + ok(needed_length == strlen(str) + 1, + "my_base64_needed_encoded_length: size %d", i); + + /* Decode */ + dst= (char *) malloc(my_base64_needed_decoded_length((int)strlen(str))); + dst_len= my_base64_decode(str, strlen(str), dst, NULL, 0); + ok(dst_len == src_len, "Comparing lengths"); + + cmp= memcmp(src, dst, src_len); + 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) + { + sprintf(buf, "%.4x ", (uint) k); + for (l=0; l<8 && k+l<src_len; l++) + { + unsigned char c= src[k+l]; + sprintf(buf, "%.2x ", (unsigned)c); + } + + sprintf(buf, " "); + + for (l=0; l<8 && k+l<dst_len; l++) + { + unsigned char c= dst[k+l]; + sprintf(buf, "%.2x ", (unsigned)c); + } + diag("%s", buf); + } + diag("src length: %.8x, dst length: %.8x\n", + (uint) src_len, (uint) dst_len); + } + free(dst); + free(str); + free(src); + } + my_end(0); + return exit_status(); +} diff --git a/unittest/mysys/bitmap-t.c b/unittest/mysys/bitmap-t.c new file mode 100644 index 00000000..e8f41b32 --- /dev/null +++ b/unittest/mysys/bitmap-t.c @@ -0,0 +1,541 @@ +/* + Copyright (c) 2006, 2012, Oracle and/or its affiliates. + + 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-1335 USA + + This test was copied from the unit test inside the + mysys/my_bitmap.c file and adapted by Mats Kindahl to use the mytap + library. +*/ + +#include <my_global.h> +#include <my_sys.h> +#include <my_bitmap.h> +#include <tap.h> +#include <m_string.h> + +#define MAX_TESTED_BITMAP_SIZE 1024 + +uint get_rand_bit(uint bitsize) +{ + return (rand() % bitsize); +} + +my_bool test_set_get_clear_bit(MY_BITMAP *map, uint bitsize) +{ + uint i, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit= get_rand_bit(bitsize); + bitmap_set_bit(map, test_bit); + if (!bitmap_is_set(map, test_bit)) + goto error1; + bitmap_clear_bit(map, test_bit); + if (bitmap_is_set(map, test_bit)) + goto error2; + } + return FALSE; +error1: + printf("Error in set bit, bit %u, bitsize = %u", test_bit, bitsize); + return TRUE; +error2: + printf("Error in clear bit, bit %u, bitsize = %u", test_bit, bitsize); + return TRUE; +} + +my_bool test_flip_bit(MY_BITMAP *map, uint bitsize) +{ + uint i, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit= get_rand_bit(bitsize); + bitmap_flip_bit(map, test_bit); + if (!bitmap_is_set(map, test_bit)) + goto error1; + bitmap_flip_bit(map, test_bit); + if (bitmap_is_set(map, test_bit)) + goto error2; + } + return FALSE; +error1: + printf("Error in flip bit 1, bit %u, bitsize = %u", test_bit, bitsize); + return TRUE; +error2: + printf("Error in flip bit 2, bit %u, bitsize = %u", test_bit, bitsize); + return TRUE; +} + + +my_bool test_get_all_bits(MY_BITMAP *map, uint bitsize) +{ + uint i; + bitmap_set_all(map); + if (!bitmap_is_set_all(map)) + goto error1; + if (!bitmap_is_prefix(map, bitsize)) + goto error5; + bitmap_clear_all(map); + if (!bitmap_is_clear_all(map)) + goto error2; + if (!bitmap_is_prefix(map, 0)) + goto error6; + for (i=0; i<bitsize;i++) + bitmap_set_bit(map, i); + if (!bitmap_is_set_all(map)) + goto error3; + for (i=0; i<bitsize;i++) + bitmap_clear_bit(map, i); + if (!bitmap_is_clear_all(map)) + goto error4; + return FALSE; +error1: + diag("Error in set_all, bitsize = %u", bitsize); + return TRUE; +error2: + diag("Error in clear_all, bitsize = %u", bitsize); + return TRUE; +error3: + diag("Error in bitmap_is_set_all, bitsize = %u", bitsize); + return TRUE; +error4: + diag("Error in bitmap_is_clear_all, bitsize = %u", bitsize); + return TRUE; +error5: + diag("Error in set_all through set_prefix, bitsize = %u", bitsize); + return TRUE; +error6: + diag("Error in clear_all through set_prefix, bitsize = %u", bitsize); + return TRUE; +} + +my_bool test_compare_operators(MY_BITMAP *map, uint bitsize) +{ + uint i, j, test_bit1, test_bit2, test_bit3,test_bit4; + uint no_loops= bitsize > 128 ? 128 : bitsize; + MY_BITMAP map2_obj, map3_obj; + MY_BITMAP *map2= &map2_obj, *map3= &map3_obj; + my_bitmap_map map2buf[MAX_TESTED_BITMAP_SIZE]; + my_bitmap_map map3buf[MAX_TESTED_BITMAP_SIZE]; + my_bitmap_init(&map2_obj, map2buf, bitsize, FALSE); + my_bitmap_init(&map3_obj, map3buf, bitsize, FALSE); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + for (i=0; i < no_loops; i++) + { + test_bit1=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + test_bit2=get_rand_bit(bitsize); + bitmap_set_prefix(map2, test_bit2); + bitmap_intersect(map, map2); + test_bit3= test_bit2 < test_bit1 ? test_bit2 : test_bit1; + bitmap_set_prefix(map3, test_bit3); + if (!bitmap_cmp(map, map3)) + goto error1; + bitmap_clear_all(map); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + test_bit1=get_rand_bit(bitsize); + test_bit2=get_rand_bit(bitsize); + test_bit3=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + bitmap_set_prefix(map2, test_bit2); + test_bit3= test_bit2 > test_bit1 ? test_bit2 : test_bit1; + bitmap_set_prefix(map3, test_bit3); + bitmap_union(map, map2); + if (!bitmap_cmp(map, map3)) + goto error2; + bitmap_clear_all(map); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + test_bit1=get_rand_bit(bitsize); + test_bit2=get_rand_bit(bitsize); + test_bit3=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + bitmap_set_prefix(map2, test_bit2); + bitmap_xor(map, map2); + test_bit3= test_bit2 > test_bit1 ? test_bit2 : test_bit1; + test_bit4= test_bit2 < test_bit1 ? test_bit2 : test_bit1; + bitmap_set_prefix(map3, test_bit3); + for (j=0; j < test_bit4; j++) + bitmap_clear_bit(map3, j); + if (!bitmap_cmp(map, map3)) + goto error3; + bitmap_clear_all(map); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + test_bit1=get_rand_bit(bitsize); + test_bit2=get_rand_bit(bitsize); + test_bit3=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + bitmap_set_prefix(map2, test_bit2); + bitmap_subtract(map, map2); + if (test_bit2 < test_bit1) + { + bitmap_set_prefix(map3, test_bit1); + for (j=0; j < test_bit2; j++) + bitmap_clear_bit(map3, j); + } + if (!bitmap_cmp(map, map3)) + goto error4; + bitmap_clear_all(map); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + test_bit1=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + bitmap_invert(map); + bitmap_set_all(map3); + for (j=0; j < test_bit1; j++) + bitmap_clear_bit(map3, j); + if (!bitmap_cmp(map, map3)) + goto error5; + bitmap_clear_all(map); + bitmap_clear_all(map3); + } + return FALSE; +error1: + diag("intersect error bitsize=%u,size1=%u,size2=%u", bitsize, + test_bit1,test_bit2); + return TRUE; +error2: + diag("union error bitsize=%u,size1=%u,size2=%u", bitsize, + test_bit1,test_bit2); + return TRUE; +error3: + diag("xor error bitsize=%u,size1=%u,size2=%u", bitsize, + test_bit1,test_bit2); + return TRUE; +error4: + diag("subtract error bitsize=%u,size1=%u,size2=%u", bitsize, + test_bit1,test_bit2); + return TRUE; +error5: + diag("invert error bitsize=%u,size=%u", bitsize, + test_bit1); + return TRUE; +} + +my_bool test_count_bits_set(MY_BITMAP *map, uint bitsize) +{ + uint i, bit_count=0, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + if (!bitmap_is_set(map, test_bit)) + { + bitmap_set_bit(map, test_bit); + bit_count++; + } + } + if (bit_count==0 && bitsize > 0) + goto error1; + if (bitmap_bits_set(map) != bit_count) + goto error2; + return FALSE; +error1: + diag("No bits set bitsize = %u", bitsize); + return TRUE; +error2: + diag("Wrong count of bits set, bitsize = %u", bitsize); + return TRUE; +} + +my_bool test_get_first_bit(MY_BITMAP *map, uint bitsize) +{ + 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); + bitmap_set_bit(map, test_bit); + if (bitmap_get_first_set(map) != test_bit) + goto error1; + bitmap_set_all(map); + bitmap_clear_bit(map, test_bit); + if (bitmap_get_first(map) != test_bit) + goto error2; + bitmap_clear_all(map); + } + return FALSE; +error1: + diag("get_first_set error bitsize=%u,prefix_size=%u",bitsize,test_bit); + return TRUE; +error2: + diag("get_first error bitsize= %u, prefix_size= %u",bitsize,test_bit); + return TRUE; +} + +my_bool test_get_next_bit(MY_BITMAP *map, uint bitsize) +{ + uint i, j, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + for (j=0; j < test_bit; j++) + bitmap_set_next(map); + if (!bitmap_is_prefix(map, test_bit)) + goto error1; + bitmap_clear_all(map); + } + return FALSE; +error1: + diag("get_next error bitsize= %u, prefix_size= %u", bitsize,test_bit); + return TRUE; +} + +my_bool test_prefix(MY_BITMAP *map, uint bitsize) +{ + uint i, j, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit); + if (!bitmap_is_prefix(map, test_bit)) + goto error1; + bitmap_clear_all(map); + for (j=0; j < test_bit; j++) + bitmap_set_bit(map, j); + if (!bitmap_is_prefix(map, test_bit)) + goto error2; + bitmap_set_all(map); + for (j=bitsize - 1; ~(j-test_bit); j--) + bitmap_clear_bit(map, j); + if (!bitmap_is_prefix(map, test_bit)) + 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); + return TRUE; +error2: + diag("prefix2 error bitsize = %u, prefix_size = %u", bitsize,test_bit); + return TRUE; +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 (my_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 (my_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; + my_bitmap_map buf[MAX_TESTED_BITMAP_SIZE]; + if (my_bitmap_init(&map, buf, bitsize, FALSE)) + { + diag("init error for bitsize %d", bitsize); + goto error; + } + if (test_set_get_clear_bit(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_flip_bit(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_get_all_bits(&map, bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_compare_operators(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_count_bits_set(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_get_first_bit(&map,bitsize)) + goto error; + 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; +} + +int main(int argc __attribute__((unused)),char *argv[]) +{ + int i; + int const min_size = 1; + int const max_size = MAX_TESTED_BITMAP_SIZE; + MY_INIT(argv[0]); + + 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); + my_end(0); + return exit_status(); +} diff --git a/unittest/mysys/byte_order-t.c b/unittest/mysys/byte_order-t.c new file mode 100644 index 00000000..d4481a41 --- /dev/null +++ b/unittest/mysys/byte_order-t.c @@ -0,0 +1,102 @@ +/* Copyright (c) 2019, MariaDB + + 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 */ + +/** + @file + + Unit tests for serialization and deserialization functions +*/ + +#include "tap.h" + +#include "my_byteorder.h" +#include "myisampack.h" +#include "m_string.h" + +void test_byte_order() +{ + MY_ALIGNED(CPU_LEVEL1_DCACHE_LINESIZE) + uchar buf[CPU_LEVEL1_DCACHE_LINESIZE * 2]; + + uchar *aligned= buf; + uchar *not_aligned= buf + CPU_LEVEL1_DCACHE_LINESIZE - 1; + +#define TEST(STORE_NAME, LOAD_NAME, TYPE, VALUE, BYTES) \ + { \ + TYPE value= VALUE; \ + uchar bytes[]= BYTES; \ + STORE_NAME(aligned, value); \ + ok(!memcmp(aligned, bytes, sizeof(bytes)), "aligned\t\t" #STORE_NAME); \ + ok(LOAD_NAME(aligned) == value, "aligned\t\t" #LOAD_NAME); \ + STORE_NAME(not_aligned, value); \ + ok(!memcmp(not_aligned, bytes, sizeof(bytes)), \ + "not aligned\t" #STORE_NAME); \ + ok(LOAD_NAME(not_aligned) == value, "not aligned\t" #LOAD_NAME); \ + } + +#define ARRAY_2(A, B) {A, B} +#define ARRAY_3(A, B, C) {A, B, C} +#define ARRAY_4(A, B, C, D) {A, B, C, D} +#define ARRAY_5(A, B, C, D, E) {A, B, C, D, E} +#define ARRAY_6(A, B, C, D, E, F) {A, B, C, D, E, F} +#define ARRAY_7(A, B, C, D, E, F, G) {A, B, C, D, E, F, G} +#define ARRAY_8(A, B, C, D, E, F, G, H) {A, B, C, D, E, F, G, H} + + TEST(int2store, sint2korr, int16, 0x0201, ARRAY_2(1, 2)); + TEST(int3store, sint3korr, int32, 0xffffffff, ARRAY_3(0xff, 0xff, 0xff)); + TEST(int3store, sint3korr, int32, 0x030201, ARRAY_3(1, 2, 3)); + TEST(int4store, sint4korr, int32, 0xffffffff, + ARRAY_4(0xff, 0xff, 0xff, 0xff)); + TEST(int4store, sint4korr, int32, 0x04030201, ARRAY_4(1, 2, 3, 4)); + TEST(int8store, sint8korr, longlong, 0x0807060504030201, + ARRAY_8(1, 2, 3, 4, 5, 6, 7, 8)); + TEST(int8store, sint8korr, longlong, 0xffffffffffffffff, + ARRAY_8(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)); + + TEST(int2store, uint2korr, uint16, 0x0201, ARRAY_2(1, 2)); + TEST(int3store, uint3korr, uint32, 0x030201, ARRAY_3(1, 2, 3)); + TEST(int4store, uint4korr, uint32, 0x04030201, ARRAY_4(1, 2, 3, 4)); + TEST(int5store, uint5korr, ulonglong, 0x0504030201, ARRAY_5(1, 2, 3, 4, 5)); + TEST(int6store, uint6korr, ulonglong, 0x060504030201, + ARRAY_6(1, 2, 3, 4, 5, 6)); + TEST(int8store, uint8korr, ulonglong, 0x0807060504030201, + ARRAY_8(1, 2, 3, 4, 5, 6, 7, 8)); + + TEST(mi_int5store, mi_uint5korr, ulonglong, 0x0504030201, + ARRAY_5(5, 4, 3, 2, 1)); + TEST(mi_int6store, mi_uint6korr, ulonglong, 0x060504030201, + ARRAY_6(6, 5, 4, 3, 2, 1)); + TEST(mi_int7store, mi_uint7korr, ulonglong, 0x07060504030201, + ARRAY_7(7, 6, 5, 4, 3, 2, 1)); + TEST(mi_int8store, mi_uint8korr, ulonglong, 0x0807060504030201, + ARRAY_8(8, 7, 6, 5, 4, 3, 2, 1)); + +#undef ARRAY_8 +#undef ARRAY_7 +#undef ARRAY_6 +#undef ARRAY_5 +#undef ARRAY_4 +#undef ARRAY_3 +#undef ARRAY_2 + +#undef TEST +} + +int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) +{ + plan(68); + test_byte_order(); + return exit_status(); +} diff --git a/unittest/mysys/crc32-t.c b/unittest/mysys/crc32-t.c new file mode 100644 index 00000000..9834d217 --- /dev/null +++ b/unittest/mysys/crc32-t.c @@ -0,0 +1,69 @@ +/* Copyright (c) MariaDB 2020 + + 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-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <my_crypt.h> +#include <tap.h> +#include <string.h> +#include <ctype.h> +#include <zlib.h> + +/* + Check that optimized crc32 (ieee, or ethernet polynomical) returns the same + result as zlib (not so well optimized, yet, but trustworthy) +*/ +#define DO_TEST_CRC32(crc,str) \ + ok(crc32(crc,(const Bytef *)str,(uint)(sizeof(str)-1)) == my_checksum(crc, str, sizeof(str)-1), "crc32 '%s'",str) + +/* Check that CRC32-C calculation returns correct result*/ +#define DO_TEST_CRC32C(crc,str,expected) \ + do { \ + unsigned int v = my_crc32c(crc, str, sizeof(str)-1); \ + printf("crc32(%u,'%s',%zu)=%u\n",crc,str,sizeof(str)-1,v); \ + ok(expected == my_crc32c(crc, str, sizeof(str)-1),"crc32c '%s'",str); \ + }while(0) + + +#define LONG_STR "1234567890234568900212345678901231213123321212123123123123123"\ + "............................................................................." \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" \ + "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" + +int main(int argc __attribute__((unused)),char *argv[]) +{ + MY_INIT(argv[0]); + plan(14); + printf("%s\n",my_crc32c_implementation()); + DO_TEST_CRC32(0,""); + DO_TEST_CRC32(1,""); + DO_TEST_CRC32(0,"12345"); + DO_TEST_CRC32(1,"12345"); + DO_TEST_CRC32(0,"1234567890123456789"); + DO_TEST_CRC32(0, LONG_STR); + ok(0 == my_checksum(0, NULL, 0) , "crc32 data = NULL, length = 0"); + + DO_TEST_CRC32C(0,"", 0); + DO_TEST_CRC32C(1,"", 1); + DO_TEST_CRC32C(0, "12345", 416359221); + DO_TEST_CRC32C(1, "12345", 549473433); + DO_TEST_CRC32C(0, "1234567890123456789", 2366987449U); + DO_TEST_CRC32C(0, LONG_STR, 3009234172U); + ok(0 == my_crc32c(0, NULL, 0), "crc32c data = NULL, length = 0"); + + my_end(0); + return exit_status(); +} diff --git a/unittest/mysys/dynstring-t.c b/unittest/mysys/dynstring-t.c new file mode 100644 index 00000000..194d435d --- /dev/null +++ b/unittest/mysys/dynstring-t.c @@ -0,0 +1,74 @@ +/* Copyright (c) 2016, MariaDB + + 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-1335 USA */ + +#include <my_global.h> +#include <m_string.h> +#include <my_sys.h> +#include <tap.h> + +DYNAMIC_STRING str1; + +static void check(const char *res) +{ + ok(strcmp(str1.str, res) == 0, "strcmp: %s", str1.str); + str1.length= 0; +} + +int main(void) +{ + plan(23); + + IF_WIN(skip_all("Test of POSIX shell escaping rules, not for CMD.EXE\n"), ); + + ok(init_dynamic_string(&str1, NULL, 0, 32) == 0, "init"); + + ok(dynstr_append_os_quoted(&str1, "test1", NULL) == 0, "append"); + check("'test1'"); + + ok(dynstr_append_os_quoted(&str1, "con", "cat", NULL) == 0, "append"); + check("'concat'"); + + ok(dynstr_append_os_quoted(&str1, "", NULL) == 0, "append"); + check("''"); + + ok(dynstr_append_os_quoted(&str1, "space inside", NULL) == 0, "append"); + check("'space inside'"); + + ok(dynstr_append_os_quoted(&str1, "single'quote", NULL) == 0, "append"); + check("'single'\"'\"'quote'"); + + ok(dynstr_append_os_quoted(&str1, "many'single'quotes", NULL) == 0, "append"); + check("'many'\"'\"'single'\"'\"'quotes'"); + + ok(dynstr_append_os_quoted(&str1, "'single quoted'", NULL) == 0, "append"); + check("''\"'\"'single quoted'\"'\"''"); + + ok(dynstr_append_os_quoted(&str1, "double\"quote", NULL) == 0, "append"); + check("'double\"quote'"); + + ok(dynstr_append_os_quoted(&str1, "mixed\"single'and\"double'quotes", NULL) == 0, "append"); + check("'mixed\"single'\"'\"'and\"double'\"'\"'quotes'"); + + ok(dynstr_append_os_quoted(&str1, "back\\space", NULL) == 0, "append"); + check("'back\\space'"); + + ok(dynstr_append_os_quoted(&str1, "backspace\\'and\\\"quote", NULL) == 0, "append"); + check("'backspace\\'\"'\"'and\\\"quote'"); + + dynstr_free(&str1); + + return exit_status(); +} + diff --git a/unittest/mysys/lf-t.c b/unittest/mysys/lf-t.c new file mode 100644 index 00000000..088ecadd --- /dev/null +++ b/unittest/mysys/lf-t.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2008, 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 + 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-1335 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; + +int with_my_thread_init=0; + +/* + pin allocator - alloc and release an element in a loop +*/ +pthread_handler_t test_lf_pinbox(void *arg) +{ + int m= *(int *)arg; + LF_PINS *pins; + + if (with_my_thread_init) + my_thread_init(); + + pins= lf_pinbox_get_pins(&lf_allocator.pinbox); + + for (; m ; m--) + { + lf_pinbox_put_pins(pins); + pins= lf_pinbox_get_pins(&lf_allocator.pinbox); + } + lf_pinbox_put_pins(pins); + + if (with_my_thread_init) + 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; + + if (with_my_thread_init) + 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 + } + pthread_mutex_unlock(&mutex); + + if (with_my_thread_init) + my_thread_end(); + return 0; +} + +my_bool do_sum(void *num, void *acc) +{ + *(int *)acc += *(int *)num; + 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, scans= 0; + LF_PINS *pins; + + if (with_my_thread_init) + 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++; + } + else + { + int unused= 0; + lf_hash_iterate(&lf_hash, pins, do_sum, &unused); + scans++; + } + } + 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, %d scans", + lf_hash.alloc.mallocs, lf_hash.alloc.pinbox.pins_in_array, + lf_hash.size, inserts, scans); + bad|= lf_hash.count; + } + pthread_mutex_unlock(&mutex); + if (with_my_thread_init) + my_thread_end(); + return 0; +} + + +void do_tests() +{ + plan(6); + + 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); + + with_my_thread_init= 1; + test_concurrently("lf_pinbox (with my_thread_init)", test_lf_pinbox, N= THREADS, CYCLES); + test_concurrently("lf_alloc (with my_thread_init)", test_lf_alloc, N= THREADS, CYCLES); + test_concurrently("lf_hash (with my_thread_init)", test_lf_hash, N= THREADS, CYCLES); + + with_my_thread_init= 0; + test_concurrently("lf_pinbox (without my_thread_init)", test_lf_pinbox, N= THREADS, CYCLES); + test_concurrently("lf_alloc (without my_thread_init)", test_lf_alloc, N= THREADS, CYCLES); + test_concurrently("lf_hash (without my_thread_init)", test_lf_hash, N= THREADS, CYCLES); + + 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 00000000..6e2c9b4c --- /dev/null +++ b/unittest/mysys/ma_dyncol-t.c @@ -0,0 +1,881 @@ +/* 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; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_NULL; + mariadb_dyncol_value_init(&res); + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= (res.type == DYN_COL_NULL); +err: + ok(rc, "%s", "NULL"); + /* cleanup */ + mariadb_dyncol_free(&str); +} + +void test_value_single_uint(ulonglong num, const char *name) +{ + int rc= FALSE; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_UINT; + val.x.ulong_value= num; + mariadb_dyncol_value_init(&res); + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= (res.type == DYN_COL_UINT) && (res.x.ulong_value == num); + num= res.x.ulong_value; +err: + ok(rc, "%s - %llu", name, num); + /* cleanup */ + mariadb_dyncol_free(&str); +} + +void test_value_single_sint(longlong num, const char *name) +{ + int rc= FALSE; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_INT; + val.x.long_value= num; + mariadb_dyncol_value_init(&res); + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= (res.type == DYN_COL_INT) && (res.x.long_value == num); + num= res.x.ulong_value; +err: + ok(rc, "%s - %lld", name, num); + /* cleanup */ + mariadb_dyncol_free(&str); +} + + +void test_value_single_double(double num, const char *name) +{ + int rc= FALSE; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_DOUBLE; + val.x.double_value= num; + mariadb_dyncol_value_init(&res); + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= (res.type == DYN_COL_DOUBLE) && (res.x.double_value == num); + num= res.x.double_value; +err: + ok(rc, "%s - %lf", name, num); + /* cleanup */ + mariadb_dyncol_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; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + + /* init values */ + mariadb_dyncol_prepare_decimal(&val); // special procedure for decimal!!! + if (string2decimal(num, &val.x.decimal.value, &end) != E_DEC_OK) + goto err; + mariadb_dyncol_value_init(&res); + + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_DECIMAL) && + (decimal_cmp(&res.x.decimal.value, &val.x.decimal.value) == 0)); + decimal2string(&res.x.decimal.value, buff, &length, 0, 0, ' '); +err: + ok(rc, "%s - %s", num, buff); + /* cleanup */ + mariadb_dyncol_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_utf8mb3 + &my_charset_utf8mb3_general_ci, +#ifdef HAVE_UCA_COLLATIONS + &my_charset_utf8mb3_unicode_ci, +#endif + &my_charset_utf8mb3_bin, +#endif +}; + + +void test_value_single_string(const char *string, size_t len, + CHARSET_INFO *cs) +{ + int rc= FALSE; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + + /* init values */ + val.type= DYN_COL_STRING; + val.x.string.value.str= (char*)string; + val.x.string.value.length= len; + val.x.string.charset= cs; + mariadb_dyncol_value_init(&res); + + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_STRING) && + (res.x.string.value.length == len) && + (memcmp(res.x.string.value.str, string, len) == 0) && + (res.x.string.charset->number == cs->number)); +err: + ok(rc, "'%s' - '%s' %u %u-%s", string, + res.x.string.value.str, (uint)res.x.string.value.length, + (uint)res.x.string.charset->number, res.x.string.charset->coll_name.str); + /* cleanup */ + val.x.string.value.str= NULL; // we did not allocated it + mariadb_dyncol_free(&str); +} + +void test_value_single_date(uint year, uint month, uint day, const char *name) +{ + int rc= FALSE; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_DATE; + val.x.time_value.time_type= MYSQL_TIMESTAMP_DATE; + val.x.time_value.year= year; + val.x.time_value.month= month; + val.x.time_value.day= day; + mariadb_dyncol_value_init(&res); + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_DATE) && + (res.x.time_value.time_type == MYSQL_TIMESTAMP_DATE) && + (res.x.time_value.year == year) && + (res.x.time_value.month == month) && + (res.x.time_value.day == day)); +err: + ok(rc, "%s - %04u-%02u-%02u", name, year, month, day); + /* cleanup */ + mariadb_dyncol_free(&str); +} + +void test_value_single_time(uint neg, uint hour, uint minute, uint second, + uint mic, const char *name) +{ + int rc= FALSE; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_TIME; + val.x.time_value.time_type= MYSQL_TIMESTAMP_TIME; + val.x.time_value.neg= neg; + val.x.time_value.hour= hour; + val.x.time_value.minute= minute; + val.x.time_value.second= second; + val.x.time_value.second_part= mic; + mariadb_dyncol_value_init(&res); + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_TIME) && + (res.x.time_value.time_type == MYSQL_TIMESTAMP_TIME) && + (res.x.time_value.neg == (int)neg) && + (res.x.time_value.hour == hour) && + (res.x.time_value.minute == minute) && + (res.x.time_value.second == second) && + (res.x.time_value.second_part == mic)); +err: + ok(rc, "%s - %c%02u:%02u:%02u.%06u", name, (neg ? '-' : '+'), + hour, minute, second, mic); + /* cleanup */ + mariadb_dyncol_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; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + /* init values */ + val.type= DYN_COL_DATETIME; + val.x.time_value.time_type= MYSQL_TIMESTAMP_DATETIME; + val.x.time_value.neg= neg; + val.x.time_value.year= year; + val.x.time_value.month= month; + val.x.time_value.day= day; + val.x.time_value.hour= hour; + val.x.time_value.minute= minute; + val.x.time_value.second= second; + val.x.time_value.second_part= mic; + mariadb_dyncol_value_init(&res); + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 1, ids, &val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + if (mariadb_dyncol_get_num(&str, 1, &res)) + goto err; + rc= ((res.type == DYN_COL_DATETIME) && + (res.x.time_value.time_type == MYSQL_TIMESTAMP_DATETIME) && + (res.x.time_value.neg == (int)neg) && + (res.x.time_value.year == year) && + (res.x.time_value.month == month) && + (res.x.time_value.day == day) && + (res.x.time_value.hour == hour) && + (res.x.time_value.minute == minute) && + (res.x.time_value.second == second) && + (res.x.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 */ + mariadb_dyncol_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].x.ulong_value= num0; + val[1].type= DYN_COL_INT; + val[1].x.long_value= num1; + val[2].type= DYN_COL_DOUBLE; + val[2].x.double_value= num2; + mariadb_dyncol_prepare_decimal(val + 3); // special procedure for decimal!!! + if (string2decimal(num3, &val[3].x.decimal.value, &end3) != E_DEC_OK) + goto err; + val[4].type= DYN_COL_STRING; + val[4].x.string.value.str= (char*)string4; + val[4].x.string.value.length= len4; + val[4].x.string.charset= cs4; + val[5].type= DYN_COL_DATE; + val[5].x.time_value.time_type= MYSQL_TIMESTAMP_DATE; + val[5].x.time_value.year= year5; + val[5].x.time_value.month= month5; + val[5].x.time_value.day= day5; + val[6].type= DYN_COL_TIME; + val[6].x.time_value.time_type= MYSQL_TIMESTAMP_TIME; + val[6].x.time_value.neg= neg6; + val[6].x.time_value.hour= hour6; + val[6].x.time_value.minute= minute6; + val[6].x.time_value.second= second6; + val[6].x.time_value.second_part= mic6; + val[7].type= DYN_COL_DATETIME; + val[7].x.time_value.time_type= MYSQL_TIMESTAMP_DATETIME; + val[7].x.time_value.neg= neg7; + val[7].x.time_value.year= year7; + val[7].x.time_value.month= month7; + val[7].x.time_value.day= day7; + val[7].x.time_value.hour= hour7; + val[7].x.time_value.minute= minute7; + val[7].x.time_value.second= second7; + val[7].x.time_value.second_part= mic7; + val[8].type= DYN_COL_NULL; + for (i= 0; i < 9; i++) + mariadb_dyncol_value_init(res + i); + /* create column */ + if (mariadb_dyncol_create_many_num(&str, 9, column_numbers, val, 1)) + goto err; + dynstr_append(&str, "\1"); str.length--; //check for overflow + /* read column */ + for (i= 0; i < 9; i++) + if (mariadb_dyncol_get_num(&str, column_numbers[i], res + i)) + goto err; + rc= ((res[0].type == DYN_COL_UINT) && + (res[0].x.ulong_value == num0) && + (res[1].type == DYN_COL_INT) && + (res[1].x.long_value == num1) && + (res[2].type == DYN_COL_DOUBLE) && + (res[2].x.double_value == num2) && + (res[3].type == DYN_COL_DECIMAL) && + (decimal_cmp(&res[3].x.decimal.value, &val[3].x.decimal.value) == 0) && + (res[4].type == DYN_COL_STRING) && + (res[4].x.string.value.length == len4) && + (memcmp(res[4].x.string.value.str, string4, len4) == 0) && + (res[4].x.string.charset->number == cs4->number) && + (res[5].type == DYN_COL_DATE) && + (res[5].x.time_value.time_type == MYSQL_TIMESTAMP_DATE) && + (res[5].x.time_value.year == year5) && + (res[5].x.time_value.month == month5) && + (res[5].x.time_value.day == day5) && + (res[6].type == DYN_COL_TIME) && + (res[6].x.time_value.time_type == MYSQL_TIMESTAMP_TIME) && + (res[6].x.time_value.neg == (int)neg6) && + (res[6].x.time_value.hour == hour6) && + (res[6].x.time_value.minute == minute6) && + (res[6].x.time_value.second == second6) && + (res[6].x.time_value.second_part == mic6) && + (res[7].type == DYN_COL_DATETIME) && + (res[7].x.time_value.time_type == MYSQL_TIMESTAMP_DATETIME) && + (res[7].x.time_value.neg == (int)neg7) && + (res[7].x.time_value.year == year7) && + (res[7].x.time_value.month == month7) && + (res[7].x.time_value.day == day7) && + (res[7].x.time_value.hour == hour7) && + (res[7].x.time_value.minute == minute7) && + (res[7].x.time_value.second == second7) && + (res[7].x.time_value.second_part == mic7) && + (res[8].type == DYN_COL_NULL)); +err: + ok(rc, "%s", name); + /* cleanup */ + val[4].x.string.value.str= NULL; // we did not allocated it + mariadb_dyncol_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 (!mariadb_dyncol_create_many_num(&str, 5, column_numbers, val, 1)) + goto err; + rc= TRUE; +err: + ok(rc, "%s", "same column numbers check"); + /* cleanup */ + mariadb_dyncol_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.x.ulong_value= column_values[0]; + if (mariadb_dyncol_create_many_num(&str, 1, column_numbers, &val, 1)) + goto err; + for (i= 1; i < all; i++) + { + val.type= (null_values[i] ? DYN_COL_NULL : DYN_COL_UINT); + val.x.ulong_value= column_values[i]; + if (mariadb_dyncol_update_many_num(&str, 1, column_numbers +i, &val)) + goto err; + + /* check value(s) */ + for (j= i; j >= (i < only_add ? 0 : i); j--) + { + if (mariadb_dyncol_get_num(&str, column_numbers[j], &val)) + goto err; + if (null_values[j]) + { + if (val.type != DYN_COL_NULL || + mariadb_dyncol_exists_num(&str, column_numbers[j]) == ER_DYNCOL_YES) + goto err; + } + else + { + if (val.type != DYN_COL_UINT || + val.x.ulong_value != column_values[j] || + mariadb_dyncol_exists_num(&str, column_numbers[j]) == ER_DYNCOL_NO) + goto err; + } + } + if (i < only_add) + { + uint elements, *num; + if (mariadb_dyncol_list_num(&str, &elements, &num)) + { + my_free(num); + goto err; + } + /* cross check arrays */ + if ((int)elements != i + 1) + { + my_free(num); + goto err; + } + for(j= 0; j < i + 1; j++) + { + int k; + for(k= 0; + k < i + 1 && column_numbers[j] != num[k]; + k++); + if (k >= i + 1) + { + my_free(num); + goto err; + } + for(k= 0; + k < i + 1 && column_numbers[k] != num[j]; + k++); + if (k >= i + 1) + { + my_free(num); + goto err; + } + } + my_free(num); + } + } + + rc= TRUE; +err: + ok(rc, "%s", "add/delete/update"); + /* cleanup */ + mariadb_dyncol_free(&str); +} + +void test_empty_string() +{ + DYNAMIC_COLUMN_VALUE val, res; + DYNAMIC_COLUMN str; + uint *array_of_uint; + uint number_of_uint; + int rc; + uint ids[1]= {1}; + DYNAMIC_COLUMN_VALUE vals[1]; + /* empty string */ + bzero(&str, sizeof(str)); + + rc= mariadb_dyncol_get_num(&str, 1, &res); + ok( (rc == ER_DYNCOL_OK) && (res.type == DYN_COL_NULL), "%s", "empty get"); + + vals[0].type= DYN_COL_NULL; + rc= mariadb_dyncol_update_many_num(&str, 1, ids, vals); + ok( (rc == ER_DYNCOL_OK) && (str.str == 0), "%s", "empty delete"); + + rc= mariadb_dyncol_exists_num(&str, 1); + ok( (rc == ER_DYNCOL_NO), "%s", "empty exists"); + + rc= mariadb_dyncol_list_num(&str, &number_of_uint, &array_of_uint); + ok( (rc == ER_DYNCOL_OK) && (number_of_uint == 0) && (str.str == 0), + "%s", "empty list"); + + val.type= DYN_COL_UINT; + val.x.ulong_value= 1212; + rc= mariadb_dyncol_update_many_num(&str, 1, ids, &val); + if (rc == ER_DYNCOL_OK) + rc= mariadb_dyncol_get_num(&str, 1, &res); + ok( (rc == ER_DYNCOL_OK) && (str.str != 0) && + (res.type == DYN_COL_UINT) && (res.x.ulong_value == val.x.ulong_value), + "%s", "empty update"); + mariadb_dyncol_free(&str); +} + +static void test_mdev_4994() +{ + DYNAMIC_COLUMN dyncol; + LEX_STRING key= {0,0}; + DYNAMIC_COLUMN_VALUE val; + int rc; + + val.type= DYN_COL_NULL; + + mariadb_dyncol_init(&dyncol); + rc= mariadb_dyncol_create_many_named(&dyncol, 1, &key, &val, 0); /* crash */ + ok( (rc == ER_DYNCOL_OK), "%s", "test_mdev_4994"); + mariadb_dyncol_free(&dyncol); +} + +static void test_mdev_4995() +{ + DYNAMIC_COLUMN dyncol; + uint column_count= 5; + int rc; + + mariadb_dyncol_init(&dyncol); + rc= mariadb_dyncol_column_count(&dyncol,&column_count); + + ok( (rc == ER_DYNCOL_OK), "%s", "test_mdev_4995"); +} + +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].x.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].x.ulong_value= update_values[i]; + } + } + for (i= 0; i < result_count; i++) + { + res[i].type= DYN_COL_UINT; + res[i].x.ulong_value= result_values[i]; + } + if (mariadb_dyncol_create_many_num(&str1, column_count, column_numbers, val, 1)) + goto err; + if (mariadb_dyncol_update_many_num(&str1, update_count, update_numbers, upd)) + goto err; + if (mariadb_dyncol_create_many_num(&str2, result_count, result_numbers, res, 1)) + goto err; + if (str1.length == str2.length && + memcmp(str1.str, str2.str, str1.length) ==0) + rc= TRUE; + +err: + ok(rc, "%s", "update_many"); + /* cleanup */ + free(val); + free(upd); + free(res); + mariadb_dyncol_free(&str1); + mariadb_dyncol_free(&str2); +} + +static void test_mdev_9773() +{ + int rc; + uint i; + uint num_keys[5]= {1,2,3,4,5}; + char const *strval[]= {"Val1", "Val2", "Val3", "Val4", "Val5"}; + DYNAMIC_COLUMN_VALUE vals[5]; + DYNAMIC_COLUMN dynstr; + uint unpack_columns= 0; + MYSQL_LEX_STRING *unpack_keys= 0; + DYNAMIC_COLUMN_VALUE *unpack_vals= 0; + + for (i = 0; i < 5; i++) + { + vals[i].type= DYN_COL_STRING; + vals[i].x.string.value.str= (char *)strval[i]; + vals[i].x.string.value.length= strlen(strval[i]); + vals[i].x.string.charset= &my_charset_latin1; + } + + mariadb_dyncol_init(&dynstr); + + /* create numeric */ + rc= mariadb_dyncol_create_many_num(&dynstr, 5, num_keys, vals, 1); + + if (rc == ER_DYNCOL_OK) + rc= mariadb_dyncol_unpack(&dynstr, &unpack_columns, &unpack_keys, + &unpack_vals); + ok (rc == ER_DYNCOL_OK && unpack_columns == 5, "5 fields unpacked"); + for (i = 0; i < unpack_columns; i++) + { + ok(memcmp(unpack_vals[i].x.string.value.str, + vals[i].x.string.value.str, vals[i].x.string.value.length) == 0, + "unpack %u", i); + } + + my_free(unpack_keys); + my_free(unpack_vals); + mariadb_dyncol_free(&dynstr); +} + +int main(int argc __attribute__((unused)), char **argv) +{ + uint i; + char *big_string= (char *)malloc(1024*1024); + + MY_INIT(argv[0]); + plan(68); + + 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(0xffffffffffffffffULL, "0xffffffffffffffff"); + test_value_single_uint(0xaaaaaaaaaaaaaaaaULL, "0xaaaaaaaaaaaaaaaa"); + test_value_single_uint(0x5555555555555555ULL, "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(0x7fffffffffffffffLL, "0x7fffffffffffffff"); + test_value_single_sint(0xaaaaaaaaaaaaaaaaLL, "0xaaaaaaaaaaaaaaaa"); + test_value_single_sint(0x5555555555555555LL, "0x5555555555555555"); + test_value_single_sint(0x8000000000000000LL, "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(0xffffffffffffffffULL, 0x7fffffffffffffffLL, + 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); + } + test_mdev_4994(); + test_mdev_4995(); + test_mdev_9773(); + + my_end(0); + return exit_status(); +} diff --git a/unittest/mysys/my_atomic-t.c b/unittest/mysys/my_atomic-t.c new file mode 100644 index 00000000..1995a8e8 --- /dev/null +++ b/unittest/mysys/my_atomic-t.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2006, 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 + 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-1335 USA */ + +#include "thr_template.c" +#include "my_atomic.h" + +volatile uint32 b32; +volatile int32 c32; + +/* 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; + int32 x; + for (x= ((int)(intptr)(&m)); m ; m--) + { + x= (x*m+0x87654321) & INT_MAX32; + my_atomic_add32(&bad, x); + my_atomic_add32(&bad, -x); + } + return 0; +} + +volatile int64 a64; +/* add and sub a random number in a loop. Must get 0 at the end */ +pthread_handler_t test_atomic_add64(void *arg) +{ + int m= (*(int *)arg)/2; + int64 x; + for (x= ((int64)(intptr)(&m)); m ; m--) + { + x= (x*m+0xfdecba987654321LL) & INT_MAX64; + my_atomic_add64(&a64, x); + my_atomic_add64(&a64, -x); + } + 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; + + x= my_atomic_add32(&b32, 1); + + my_atomic_add32(&bad, x); + + for (; m ; m--) + x= my_atomic_fas32(&c32, x); + + if (!x) + x= my_atomic_fas32(&c32, x); + + my_atomic_add32(&bad, -x); + + 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; + int32 x, y; + for (x= ((int)(intptr)(&m)); m ; m--) + { + y= my_atomic_load32(&bad); + x= (x*m+0x87654321) & INT_MAX32; + do { + ok= my_atomic_cas32((int32*) &bad, &y, y+x); + } while (!ok) ; + do { + ok= my_atomic_cas32((int32*) &bad, &y, y-x); + } while (!ok) ; + } + return 0; +} + + +void do_tests() +{ + plan(5); + + 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); + + { + /* + If b is not volatile, the wrong assembly code is generated on OSX Lion + as the variable is optimized away as a constant. + See Bug#62533 / Bug#13030056. + Another workaround is to specify architecture explicitly using e.g. + CFLAGS/CXXFLAGS= "-m64". + */ + volatile int64 b=0x1000200030004000LL; + a64=0; + my_atomic_add64(&a64, b); + ok(a64==b, "add64"); + } + a64=0; + test_concurrently("my_atomic_add64", test_atomic_add64, THREADS, CYCLES); + bad= (a64 != 0); +} diff --git a/unittest/mysys/my_delete-t.c b/unittest/mysys/my_delete-t.c new file mode 100644 index 00000000..7bc6cf89 --- /dev/null +++ b/unittest/mysys/my_delete-t.c @@ -0,0 +1,57 @@ +/* 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 + 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-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include "tap.h" + + +int main(int argc __attribute__((unused)),char *argv[]) +{ + char tmp_dir[MAX_PATH]; + char tmp_filename[MAX_PATH]; + HANDLE h, h2; + + MY_INIT(argv[0]); + + plan(6); + + GetTempPathA(MAX_PATH, tmp_dir); + ok(GetTempFileNameA(tmp_dir, "foo", 0, tmp_filename) != 0, "create temp file"); + ok(my_delete(tmp_filename,MYF(0)) == 0, "Delete closed file"); + + + /* Delete an open file */ + ok(GetTempFileNameA(tmp_dir, "foo", 0, tmp_filename) != 0, "create temp file 2"); + h = CreateFileA(tmp_filename, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); + ok (h != INVALID_HANDLE_VALUE || h != 0, "open temp file"); + ok(my_delete(tmp_filename, MYF(0)) == 0, "Delete open file"); + + + /* + Check if it is possible to reuse file name after delete (not all handles + to it are closed. + */ + h2 = CreateFileA(tmp_filename, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE, NULL); + ok(h2 != 0 && h2 != INVALID_HANDLE_VALUE, "Reuse file name"); + CloseHandle(h); + CloseHandle(h2); + + my_end(0); + return exit_status(); +} + diff --git a/unittest/mysys/my_getopt-t.c b/unittest/mysys/my_getopt-t.c new file mode 100644 index 00000000..6ffbfb20 --- /dev/null +++ b/unittest/mysys/my_getopt-t.c @@ -0,0 +1,389 @@ +/* Copyright (c) 2015, MariaDB Corporation + + 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_getopt.h> +#include <mysys_err.h> +#include <stdarg.h> +#include <tap.h> + +ulonglong opt_ull; +ulong opt_ul; +int arg_c, res; +char **arg_v, *arg_s[100]; + +ulong mopts_num; +char *mopts_str; +my_bool mopts_bool; +static struct my_option mopts_options[]= +{ + {"str", 0, + "Something numeric.", + &mopts_str, &mopts_str, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bool", 0, + "Something true or false", + &mopts_bool, &mopts_bool, 0, GET_BOOL, + OPT_ARG, FALSE, 0, 0, 0, 0, 0}, + {"num", 0, + "Something numeric.", + &mopts_num, &mopts_num, 0, GET_ULONG, + REQUIRED_ARG, 1000000L, 1, ULONG_MAX, 0, 2, 0}, + {"ull", 0, "ull", &opt_ull, &opt_ull, + 0, GET_ULL, REQUIRED_ARG, 1, 0, ~0ULL, 0, 0, 0}, + {"ul", 0, "ul", &opt_ul, &opt_ul, + 0, GET_ULONG, REQUIRED_ARG, 1, 0, 0xFFFFFFFF, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + +my_bool dummy_get_one_option(const struct my_option *opt __attribute__((unused)), + const char *argument __attribute__((unused)), + const char *filename __attribute__((unused))) +{ + return FALSE; +} + +void run(const char *arg, ...) +{ + va_list ap; + va_start(ap, arg); + arg_v= arg_s; + *arg_v++= (char*)"<skipped>"; + while (arg) + { + *arg_v++= (char*)arg; + arg= va_arg(ap, char*); + } + va_end(ap); + arg_c= (int)(arg_v - arg_s); + arg_v= arg_s; + res= handle_options(&arg_c, &arg_v, mopts_options, &dummy_get_one_option); +} + +int mopts1_argc= 4; +const char *mopts1_argv[]= {"mopts1", "--num=123", "--str=str", "--bool"}; +void test_mopts1() +{ + int rc; + char **av= (char **)mopts1_argv; + + rc= handle_options(&mopts1_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_mopts1 call"); + ok( (mopts_num == 122), "%s", "test_mopts1 num"); + ok( (strncmp(mopts_str, "str", 4) == 0), "%s", "test_mopts1 str"); + ok( (mopts_bool == 1), "%s", "test_mopts1 bool"); +} + +int mopts2_argc= 4; +const char *mopts2_argv[]= {"mopts2", "--num=123", "--num=124", "--bool=0"}; +void test_mopts2() +{ + int rc; + char **av= (char **)mopts2_argv; + + rc= handle_options(&mopts2_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_mopts2 call"); + ok( (mopts_num == 124), "%s", "test_mopts2 num"); + ok( (strncmp(mopts_str, "ddd", 4) == 0), "%s", "test_mopts2 str"); + ok( (mopts_bool == 0), "%s", "test_mopts2 bool"); +} + +int mopts3_argc= 4; +const char *mopts3_argv[]= {"mopts3", "--loose-foo", "--loose-loose-foo", "--enable-bool"}; +void test_mopts3() +{ + int rc; + char **av= (char **)mopts3_argv; + + rc= handle_options(&mopts3_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_mopts3 call"); + ok( (mopts_num == 1000000L), "%s", "test_mopts3 num"); + ok( (strncmp(mopts_str, "ddd", 4) == 0), "%s", "test_mopts3 str"); + ok( (mopts_bool == 1), "%s", "test_mopts3 bool"); +} + +int mopts4_argc= 3; +const char *mopts4_argv[]= {"mopts4", "--loose-str=aa", "--skip-bool"}; +void test_mopts4() +{ + int rc; + char **av= (char **)mopts4_argv; + + rc= handle_options(&mopts4_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_mopts4 call"); + ok( (mopts_num == 1000000L), "%s", "test_mopts4 num"); + ok( (strncmp(mopts_str, "aa", 3) == 0), "%s", "test_mopts4 str"); + ok( (mopts_bool == 0), "%s", "test_mopts4 bool"); +} + +int mopts5_argc= 2; +const char *mopts5_argv[]= {"mopts5", "--loose-skip-bool"}; +void test_mopts5() +{ + int rc; + char **av= (char **)mopts5_argv; + + rc= handle_options(&mopts5_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_mopts5 call"); + ok( (mopts_num == 1000000L), "%s", "test_mopts5 num"); + ok( (strncmp(mopts_str, "ddd", 4) == 0), "%s", "test_mopts5 str"); + ok( (mopts_bool == 0), "%s", "test_mopts5 bool"); +} + +int mopts6_argc= 2; +const char *mopts6_argv[]= {"mopts6", "--loose-skip-skip-bool"}; +void test_mopts6() +{ + int rc; + char **av= (char **)mopts6_argv; + + rc= handle_options(&mopts6_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_mopts6 call"); + ok( (mopts_num == 1000000L), "%s", "test_mopts6 num"); + ok( (strncmp(mopts_str, "ddd", 4) == 0), "%s", "test_mopts6 str"); + ok( (mopts_bool == 0), "%s", "test_mopts6 bool"); +} + +int mopts7_argc= 2; +const char *mopts7_argv[]= {"mopts7", "--loose-disable-skip-bool"}; +void test_mopts7() +{ + int rc; + char **av= (char **)mopts7_argv; + + rc= handle_options(&mopts7_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_mopts7 call"); + ok( (mopts_num == 1000000L), "%s", "test_mopts7 num"); + ok( (strncmp(mopts_str, "ddd", 4) == 0), "%s", "test_mopts7 str"); + ok( (mopts_bool == 0), "%s", "test_mopts7 bool"); +} + +int mopts8_argc= 2; +const char *mopts8_argv[]= {"mopts8", "--loose-disable-enable-bool"}; +void test_mopts8() +{ + int rc; + char **av= (char **)mopts8_argv; + + rc= handle_options(&mopts8_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_mopts8 call"); + ok( (mopts_num == 1000000L), "%s", "test_mopts7 num"); + ok( (strncmp(mopts_str, "ddd", 4) == 0), "%s", "test_mopts7 str"); + ok( (mopts_bool == 1), "%s", "test_mopts7 bool"); +} + +int mopts9_argc= 2; +const char *mopts9_argv[]= {"mopts9", "--foo"}; +void test_mopts9() +{ + int rc; + char **av= (char **)mopts9_argv; + + rc= handle_options(&mopts9_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc != 0), "%s", "test_mopts9 call"); +} + +int mopts10_argc= 2; +const char *mopts10_argv[]= {"mopts10", "--skip-foo"}; +void test_mopts10() +{ + int rc; + char **av= (char **)mopts10_argv; + + rc= handle_options(&mopts10_argc, &av, mopts_options, &dummy_get_one_option); + ok( (rc != 0), "%s", "test_mopts10 call"); +} + +ulong auto_num; +static struct my_option auto_options[]= +{ + {"anum", 0, + "Something numeric.", + &auto_num, &auto_num, 0, GET_ULONG | GET_AUTO, + REQUIRED_ARG, 1000000L, 1, ULONG_MAX, 0, 1, 0}, + {"num", 0, + "Something numeric.", + &mopts_num, &mopts_num, 0, GET_ULONG, + REQUIRED_ARG, 1000000L, 1, ULONG_MAX, 0, 1, 0}, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + + + +my_bool auto_get_one_option(const struct my_option *opt, + const char *argument, + const char *filename __attribute__((unused))) +{ + if (argument == autoset_my_option) + { + *((ulong*)opt->value)= 111; + } + return FALSE; +} + +int auto2_argc= 3; +const char *auto2_argv[]= {"auto2", "--num=123", "--autoset-num"}; +void test_auto2() +{ + int rc; + char **av= (char **)auto2_argv; + + rc= handle_options(&auto2_argc, &av, auto_options, &auto_get_one_option); + ok( (rc == EXIT_ARGUMENT_INVALID), "%s", "test_auto2 call"); +} + +int auto3_argc= 3; +const char *auto3_argv[]= {"auto3", "--anum=123", "--autoset-anum"}; +void test_auto3() +{ + int rc; + char **av= (char **)auto3_argv; + + rc= handle_options(&auto3_argc, &av, auto_options, &auto_get_one_option); + ok( (rc == 0), "%s", "test_auto3 call"); + ok( (mopts_num == 1000000L), "%s", "test_auto3 num"); + ok( (auto_num == 111), "%s", "test_auto3 anum"); +} + +int auto4_argc= 3; +const char *auto4_argv[]= {"auto4", "--loose-autoset-num", "--loose-autoset-anum"}; +void test_auto4() +{ + int rc; + char **av= (char **)auto4_argv; + + rc= handle_options(&auto4_argc, &av, auto_options, &auto_get_one_option); + ok( (rc == 0), "%s", "test_auto4 call"); + ok( (mopts_num == 1000000L), "%s", "test_auto4 num"); + ok( (auto_num == 111), "%s", "test_auto4 anum"); +} + +int auto5_argc= 3; +const char *auto5_argv[]= {"auto5", "--autoset-loose-num", "--autoset-loose-anum"}; +void test_auto5() +{ + int rc; + char **av= (char **)auto5_argv; + + rc= handle_options(&auto5_argc, &av, auto_options, &auto_get_one_option); + ok( (rc == 0), "%s", "test_auto5 call"); + ok( (mopts_num == 1000000L), "%s", "test_auto5 num"); + ok( (auto_num == 111), "%s", "test_auto5 anum"); +} + +int auto6_argc= 3; +const char *auto6_argv[]= {"auto6", "--autoset-anum", "--anum=123"}; +void test_auto6() +{ + int rc; + char **av= (char **)auto6_argv; + + rc= handle_options(&auto6_argc, &av, auto_options, &auto_get_one_option); + ok( (rc == 0), "%s", "test_auto6 call"); + ok( (mopts_num == 1000000L), "%s", "test_auto6 num"); + ok( (auto_num == 123), "%s", "test_auto6 anum"); +} + + +ulong max_num= ULONG_MAX; +static struct my_option max_options[]= +{ + {"num", 0, + "Something numeric.", + &mopts_num, &max_num, 0, GET_ULONG, + REQUIRED_ARG, 1000000L, 1, 1000001L, 0, 1, 0}, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + +int max1_argc= 3; +const char *max1_argv[]= {"max1", "--num=100", "--num=200"}; +void test_max1() +{ + int rc; + char **av= (char **)max1_argv; + + rc= handle_options(&max1_argc, &av, max_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_max1 call"); + ok( (mopts_num == 200), "%s", "test_max1 num"); + ok( (max_num == 1000001L), "%s", "test_max1 max_num"); +} +int max2_argc= 3; +const char *max2_argv[]= {"max2", "--maximum-num=100", "--num=200"}; +void test_max2() +{ + int rc; + char **av= (char **)max2_argv; + + rc= handle_options(&max2_argc, &av, max_options, &dummy_get_one_option); + ok( (rc == 0), "%s", "test_max2 call"); + ok( (mopts_num == 200), "%s", "test_max2 num"); + ok( (max_num == 100), "%s", "test_max2 max_num"); +} + +int main(int argc __attribute__((unused)), char **argv) +{ + MY_INIT(argv[0]); + plan(4*8 + 1*4 + 3*4 + 3*2 + 2); + + /* gcc 4.1.2 doesn't want it in the initializer, we have to do it run-time */ + mopts_options[0].def_value= (intptr)"ddd"; + + test_mopts1(); + test_mopts2(); + test_mopts3(); + test_mopts4(); + test_mopts5(); + test_mopts6(); + test_mopts7(); + test_mopts8(); + + test_mopts9(); + test_mopts10(); + test_auto2(); + + test_auto3(); + test_auto4(); + test_auto5(); + test_auto6(); + + test_max1(); + test_max2(); + + run("--ull=100", NULL); + ok(res==0 && arg_c==0 && opt_ull==100, + "res:%d, argc:%d, opt_ull:%llu", res, arg_c, opt_ull); + + run("--ull=-100", NULL); + ok(res==9 && arg_c==1 && opt_ull==0ULL, + "res:%d, argc:%d, opt_ull:%llu", res, arg_c, opt_ull); + run("--ul=-100", NULL); + ok(res==9 && arg_c==1 && opt_ul==0UL, + "res:%d, argc:%d, opt_ul:%lu", res, arg_c, opt_ul); + + my_end(0); + return exit_status(); +} diff --git a/unittest/mysys/my_malloc-t.c b/unittest/mysys/my_malloc-t.c new file mode 100644 index 00000000..0ee6b2b9 --- /dev/null +++ b/unittest/mysys/my_malloc-t.c @@ -0,0 +1,44 @@ +/* Copyright (c) 2010, 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 + 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-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include "tap.h" + +int main(int argc __attribute__((unused)),char *argv[]) +{ + void *p; + MY_INIT(argv[0]); + + plan(4); + + p= my_malloc(PSI_NOT_INSTRUMENTED, 0, MYF(0)); + ok(p != NULL, "Zero-sized block allocation."); + + p= my_realloc(PSI_NOT_INSTRUMENTED, p, 32, MYF(0)); + ok(p != NULL, "Reallocated zero-sized block."); + + p= my_realloc(PSI_NOT_INSTRUMENTED, p, 16, MYF(0)); + ok(p != NULL, "Trimmed block."); + + my_free(p); + p= NULL; + + ok((my_free(p), 1), "Free NULL pointer."); + + my_end(0); + return exit_status(); +} + diff --git a/unittest/mysys/my_rdtsc-t.c b/unittest/mysys/my_rdtsc-t.c new file mode 100644 index 00000000..81005e9e --- /dev/null +++ b/unittest/mysys/my_rdtsc-t.c @@ -0,0 +1,230 @@ +/* Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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-1335 USA */ + +/* + rdtsc3 -- multi-platform timer code + pgulutzan@mysql.com, 2005-08-29 + modified 2008-11-02 + + When you run rdtsc3, it will print the contents of + "my_timer_info". The display indicates + what timer routine is best for a given platform. + + For example, this is the display on production.mysql.com, + a 2.8GHz Xeon with Linux 2.6.17, gcc 3.3.3: + + cycles nanoseconds microseconds milliseconds ticks +------------- ------------- ------------- ------------- ------------- + 1 11 13 18 17 + 2815019607 1000000000 1000000 1049 102 + 1 1000 1 1 1 + 88 4116 3888 4092 2044 + + The first line shows routines, e.g. 1 = MY_TIMER_ROUTINE_ASM_X86. + The second line shows frequencies, e.g. 2815019607 is nearly 2.8GHz. + The third line shows resolutions, e.g. 1000 = very poor resolution. + The fourth line shows overheads, e.g. ticks takes 2044 cycles. +*/ + +#include "my_global.h" +#include "my_rdtsc.h" +#include "tap.h" + +#define LOOP_COUNT 100 + +MY_TIMER_INFO myt; + +void test_init() +{ + my_timer_init(&myt); + + diag("----- Routine ---------------"); + diag("myt.cycles.routine : %13llu", myt.cycles.routine); + diag("myt.nanoseconds.routine : %13llu", myt.nanoseconds.routine); + diag("myt.microseconds.routine : %13llu", myt.microseconds.routine); + diag("myt.milliseconds.routine : %13llu", myt.milliseconds.routine); + diag("myt.ticks.routine : %13llu", myt.ticks.routine); + + diag("----- Frequency -------------"); + diag("myt.cycles.frequency : %13llu", myt.cycles.frequency); + diag("myt.nanoseconds.frequency : %13llu", myt.nanoseconds.frequency); + diag("myt.microseconds.frequency : %13llu", myt.microseconds.frequency); + diag("myt.milliseconds.frequency : %13llu", myt.milliseconds.frequency); + diag("myt.ticks.frequency : %13llu", myt.ticks.frequency); + + diag("----- Resolution ------------"); + diag("myt.cycles.resolution : %13llu", myt.cycles.resolution); + diag("myt.nanoseconds.resolution : %13llu", myt.nanoseconds.resolution); + diag("myt.microseconds.resolution : %13llu", myt.microseconds.resolution); + diag("myt.milliseconds.resolution : %13llu", myt.milliseconds.resolution); + diag("myt.ticks.resolution : %13llu", myt.ticks.resolution); + + diag("----- Overhead --------------"); + diag("myt.cycles.overhead : %13llu", myt.cycles.overhead); + diag("myt.nanoseconds.overhead : %13llu", myt.nanoseconds.overhead); + diag("myt.microseconds.overhead : %13llu", myt.microseconds.overhead); + diag("myt.milliseconds.overhead : %13llu", myt.milliseconds.overhead); + diag("myt.ticks.overhead : %13llu", myt.ticks.overhead); + + ok(1, "my_timer_init() did not crash"); +} + +void test_cycle() +{ + ulonglong t1= my_timer_cycles(); + ulonglong t2; + int i; + int backward= 0; + int nonzero= 0; + + for (i=0 ; i < LOOP_COUNT ; i++) + { + t2= my_timer_cycles(); + if (t1 >= t2) + backward++; + if (t2 != 0) + nonzero++; + t1= t2; + } + + /* Expect at most 1 backward, the cycle value can overflow */ + ok((backward <= 1), "The cycle timer is strictly increasing"); + + if (myt.cycles.routine != 0) + ok((nonzero != 0), "The cycle timer is implemented"); + else + ok((nonzero == 0), "The cycle timer is not implemented and returns 0"); +} + +void test_nanosecond() +{ + ulonglong t1= my_timer_nanoseconds(); + ulonglong t2; + int i; + int backward= 0; + int nonzero= 0; + + for (i=0 ; i < LOOP_COUNT ; i++) + { + t2= my_timer_nanoseconds(); + if (t1 > t2) + backward++; + if (t2 != 0) + nonzero++; + t1= t2; + } + + ok((backward == 0), "The nanosecond timer is increasing"); + + if (myt.nanoseconds.routine != 0) + ok((nonzero != 0), "The nanosecond timer is implemented"); + else + ok((nonzero == 0), "The nanosecond timer is not implemented and returns 0"); +} + +void test_microsecond() +{ + ulonglong t1= my_timer_microseconds(); + ulonglong t2; + int i; + int backward= 0; + int nonzero= 0; + + for (i=0 ; i < LOOP_COUNT ; i++) + { + t2= my_timer_microseconds(); + if (t1 > t2) + backward++; + if (t2 != 0) + nonzero++; + t1= t2; + } + + ok((backward == 0), "The microsecond timer is increasing"); + + if (myt.microseconds.routine != 0) + ok((nonzero != 0), "The microsecond timer is implemented"); + else + ok((nonzero == 0), "The microsecond timer is not implemented and returns 0"); +} + +void test_millisecond() +{ + ulonglong t1= my_timer_milliseconds(); + ulonglong t2; + int i; + int backward= 0; + int nonzero= 0; + + for (i=0 ; i < LOOP_COUNT ; i++) + { + t2= my_timer_milliseconds(); + if (t1 > t2) + backward++; + if (t2 != 0) + nonzero++; + t1= t2; + } + + ok((backward == 0), "The millisecond timer is increasing"); + + if (myt.milliseconds.routine != 0) + ok((nonzero != 0), "The millisecond timer is implemented"); + else + ok((nonzero == 0), "The millisecond timer is not implemented and returns 0"); +} + +void test_tick() +{ + ulonglong t1= my_timer_ticks(); + ulonglong t2; + int i; + int backward= 0; + int nonzero= 0; + + for (i=0 ; i < LOOP_COUNT ; i++) + { + t2= my_timer_ticks(); + if (t1 > t2) + backward++; + if (t2 != 0) + nonzero++; + t1= t2; + } + + ok((backward == 0), "The tick timer is increasing"); + + if (myt.ticks.routine != 0) + ok((nonzero != 0), "The tick timer is implemented"); + else + ok((nonzero == 0), "The tick timer is not implemented and returns 0"); +} + +int main(int argc __attribute__((unused)), + char ** argv __attribute__((unused))) +{ + plan(11); + + test_init(); + test_cycle(); + test_nanosecond(); + test_microsecond(); + test_millisecond(); + test_tick(); + + return 0; +} + diff --git a/unittest/mysys/my_vsnprintf-t.c b/unittest/mysys/my_vsnprintf-t.c new file mode 100644 index 00000000..af17f6e3 --- /dev/null +++ b/unittest/mysys/my_vsnprintf-t.c @@ -0,0 +1,214 @@ +/* Copyright (c) 2003, 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 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-1335 USA */ + +#include <my_global.h> +#include <m_string.h> +#include <tap.h> + +char buf[1024]; /* let's hope that's enough */ + +static void test_w_len(const char *res, size_t buflen, const char *fmt, ...) +{ + va_list args; + size_t len; + va_start(args,fmt); + len= my_vsnprintf(buf, buflen, fmt, args); + va_end(args); + ok(strlen(res) == len && strcmp(buf, res) == 0, "\"%s\"", buf); +} + +static 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); +} + +static void test_many(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); + + for (; *res ; res++) + { + if (strlen(*res) == len && strcmp(buf, *res) == 0) + { + ok(1, "\"%s\"", buf); + return; + } + } + ok(0, "\"%s\"", buf); +} + + +int main(void) +{ + plan(48); + + 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 i 1", + "Format specifier i %i", 1); + test1("Format specifier u 2", + "Format specifier u %u", 2); + test1("Format specifier o 375", + "Format specifier o %o", 0375); + 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("Format specifier f 3.141593", + "Format specifier f %f", 3.1415926); + test1("Format specifier g 3.1416", + "Format specifier g %g", 3.1415926); + + test1("Flag '-' is ignored < 1>", + "Flag '-' is ignored <%-4d>", 1); + test1("Flag '0' works <0006>", + "Flag '0' works <%04d>", 6); + + test1("Width for strings < x> < y>", + "Width for strings <%04s> <%5s>", "x", "y"); + + test1("Negative width is ignored for strings < x> < y>", + "Negative width is ignored for strings <%-4s> <%-5s>", "x", "y"); + + test1("Precision works for strings <abcde>", + "Precision works for strings <%.5s>", "abcdef!"); + test1("Precision works for strings <ab...>", + "Precision works for strings <%.5T>", "abcdef!"); + + test1("Flag '`' (backtick) works: `abcd` `op``q` (mysql extension)", + "Flag '`' (backtick) works: %`s %`.4s (mysql extension)", + "abcd", "op`qrst"); + + test1("Flag '`' (backtick) works: `abcd` `op``q...` (mysql extension)", + "Flag '`' (backtick) works: %`T %`.7T (mysql extension)", + "abcd", "op`qrstuuuuuuuuu"); + + test1("Flag '`' (backtick) works: `abcd` `.` (mysql extension)", + "Flag '`' (backtick) works: %`T %`.1T (mysql extension)", + "abcd", "op`qrstuuuuuuuuu"); + + test1("Flag '`' (backtick) works: `abcd` `...` (mysql extension)", + "Flag '`' (backtick) works: %`T %`.3T (mysql extension)", + "abcd", "op`qrstuuuuuuuuu"); + + test1("Flag '`' (backtick) works: `abcd` `op...` (mysql extension)", + "Flag '`' (backtick) works: %`T %`.5T (mysql extension)", + "abcd", "op`qrstuuuuuuuuu"); + + test1("Flag '`' (backtick) works: `abcd` `op``...` (mysql extension)", + "Flag '`' (backtick) works: %`T %`.6T (mysql extension)", + "abcd", "op`qrstuuuuuuuuu"); + + test1("Length modifiers work: 1 * -1 * 2 * 3", + "Length modifiers work: %d * %ld * %lld * %zd", 1, -1L, 2LL, (size_t)3); + + test1("Length modifiers work: 1 * -1 * 2 * 3", + "Length modifiers work: %i * %li * %lli * %zd", 1, -1L, 2LL, (size_t)3); + + test1("long long X: 123456789abcdef0", + "long long X: %llx", 0x123456789abcdef0LL); + + 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("Asterisk '*' as a precision works: <qwe...>", + "Asterisk '*' as a precision works: <%.*T>", 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 for a precision: <qwe...>", + "Positional arguments for a precision: <%1$.*2$T>", "qwertyuiop", 6); + + test1("Positional arguments and a width: <0000ab>", + "Positional arguments and a width: <%1$06x>", 0xab); + + test1("Positional arguments octal: <7777>", + "Positional arguments octal: <%1$o>", 07777); + + /* Can't use int arguments, as they may be different size from pointers */ + + test1("Padding and %p <0x12> <0x034> <0x0000ab> < 0xcd>", + "Padding and %%p <%04p> <%05p> <%08p> <%8p>", + (void*) 0x12, (void*) 0x34, (void*) 0xab, (void*) 0xcd); + + test1("F with a width (ignored) and precision: <12.34568>", + "F with a width (ignored) and precision: <%10.5f>", 12.3456789); + test1("G with a width (ignored) and precision: <12.35>", + "G with a width (ignored) and precision: <%10.5g>", 12.3456789); + + { + /* Test that %M works */ + const char *results[]= + { + "Error 1 \"Operation not permitted\"", /* Linux */ + "Error 1 \"Not owner\"", /* Solaris */ + NullS + }; + test_many(results, "Error %M", 1); + } + + test1("M with 0 error code: 0 \"Internal error/check (Not system error)\"", + "M with 0 error code: %M", 0); + + test1("M with positional: 0 \"Internal error/check (Not system error)\"", + "M with positional: %1$M", 0); + + test1("M with width: 0 \"Internal error...", + "M with width: %.20M", 0); + test1("M with width positional: 0 \"Internal error...", + "M with width positional: %2$.*1$M", 20, 0); + + test_w_len("M small buf: 0 \"..", + 19, "M small buf: %M", 0); + test_w_len("M small buf positional: 0 \"..", + 30, "M small buf positional: %1$M", 0); + + return exit_status(); +} + diff --git a/unittest/mysys/queues-t.c b/unittest/mysys/queues-t.c new file mode 100644 index 00000000..23cb0da1 --- /dev/null +++ b/unittest/mysys/queues-t.c @@ -0,0 +1,139 @@ +/* Copyright (c) 2020, MariaDB Corporation + + 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-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <queues.h> +#include <my_rnd.h> +#include "tap.h" + +int cmp(void *arg __attribute__((unused)), uchar *a, uchar *b) +{ + return *a < *b ? -1 : *a > *b; +} + +#define rnd(R) ((uint)(my_rnd(R) * INT_MAX32)) + +#define el(Q,I) ((uint)*queue_element(Q, I)) + +my_bool verbose; + +my_bool check_queue(QUEUE *queue) +{ + char b[1024]={0}, *s, *e=b+sizeof(b)-2; + my_bool ok=1; + uint i; + + s= b + my_snprintf(b, e-b, "%x", el(queue, 1)); + for (i=2; i <= queue->elements; i++) + { + s+= my_snprintf(s, e-s, ", %x", el(queue, i)); + ok &= el(queue, i) <= el(queue, i>>1); + } + if (!ok || verbose) + diag("%s", b); + return ok; +} + +int main(int argc __attribute__((unused)), char *argv[]) +{ + QUEUE q, *queue=&q; + MY_INIT(argv[0]); + plan(19); + + verbose=1; + + init_queue(queue, 256, 0, 1, cmp, NULL, 0, 0); + queue_insert(queue, (uchar*)"\x99"); + queue_insert(queue, (uchar*)"\x19"); + queue_insert(queue, (uchar*)"\x36"); + queue_insert(queue, (uchar*)"\x17"); + queue_insert(queue, (uchar*)"\x12"); + queue_insert(queue, (uchar*)"\x05"); + queue_insert(queue, (uchar*)"\x25"); + queue_insert(queue, (uchar*)"\x09"); + queue_insert(queue, (uchar*)"\x15"); + queue_insert(queue, (uchar*)"\x06"); + queue_insert(queue, (uchar*)"\x11"); + queue_insert(queue, (uchar*)"\x01"); + queue_insert(queue, (uchar*)"\x04"); + queue_insert(queue, (uchar*)"\x13"); + queue_insert(queue, (uchar*)"\x24"); + ok(check_queue(queue), "after insert"); + queue_remove(queue, 5); + ok(check_queue(queue), "after remove 5th"); + + queue_element(queue, 1) = (uchar*)"\x01"; + queue_element(queue, 2) = (uchar*)"\x10"; + queue_element(queue, 3) = (uchar*)"\x04"; + queue_element(queue, 4) = (uchar*)"\x09"; + queue_element(queue, 5) = (uchar*)"\x13"; + queue_element(queue, 6) = (uchar*)"\x03"; + queue_element(queue, 7) = (uchar*)"\x08"; + queue_element(queue, 8) = (uchar*)"\x07"; + queue_element(queue, 9) = (uchar*)"\x06"; + queue_element(queue,10) = (uchar*)"\x12"; + queue_element(queue,11) = (uchar*)"\x05"; + queue_element(queue,12) = (uchar*)"\x02"; + queue_element(queue,13) = (uchar*)"\x11"; + queue->elements= 13; + ok(!check_queue(queue), "manually filled (queue property violated)"); + + queue_fix(queue); + ok(check_queue(queue), "fixed"); + + ok(*queue_remove_top(queue) == 0x13, "remove top 13"); + ok(*queue_remove_top(queue) == 0x12, "remove top 12"); + ok(*queue_remove_top(queue) == 0x11, "remove top 11"); + ok(*queue_remove_top(queue) == 0x10, "remove top 10"); + ok(*queue_remove_top(queue) == 0x09, "remove top 9"); + ok(*queue_remove_top(queue) == 0x08, "remove top 8"); + ok(*queue_remove_top(queue) == 0x07, "remove top 7"); + ok(*queue_remove_top(queue) == 0x06, "remove top 6"); + ok(*queue_remove_top(queue) == 0x05, "remove top 5"); + ok(*queue_remove_top(queue) == 0x04, "remove top 4"); + ok(*queue_remove_top(queue) == 0x03, "remove top 3"); + ok(*queue_remove_top(queue) == 0x02, "remove top 2"); + ok(*queue_remove_top(queue) == 0x01, "remove top 1"); + + /* random test */ + { + int i, res; + struct my_rnd_struct rand; + my_rnd_init(&rand, (ulong)(intptr)&i, (ulong)(intptr)argv); + verbose=0; + + for (res= i=1; i <= 250; i++) + { + uchar *s=alloca(2); + *s= rnd(&rand) % 251; + queue_insert(queue, s); + res &= check_queue(queue); + } + ok(res, "inserted 250"); + + while (queue->elements) + { + queue_remove(queue, (rnd(&rand) % queue->elements) + 1); + res &= check_queue(queue); + } + ok(res, "removed 250"); + } + + delete_queue(queue); + my_end(0); + return exit_status(); +} + diff --git a/unittest/mysys/stacktrace-t.c b/unittest/mysys/stacktrace-t.c new file mode 100644 index 00000000..d8408f80 --- /dev/null +++ b/unittest/mysys/stacktrace-t.c @@ -0,0 +1,69 @@ + +/* Copyright (c) 2020, MariaDB Corporation. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <stdio.h> +#include <my_stacktrace.h> +#include <tap.h> + +char b_bss[10]; + +void test_my_safe_print_str() +{ + char b_stack[10]; + char *b_heap= strdup("LEGAL"); + memcpy(b_stack, "LEGAL", 6); + memcpy(b_bss, "LEGAL", 6); + +#ifdef HAVE_STACKTRACE +#ifndef __SANITIZE_ADDRESS__ + fprintf(stderr, "\n===== stack =====\n"); + my_safe_print_str(b_stack, 65535); + fprintf(stderr, "\n===== heap =====\n"); + my_safe_print_str(b_heap, 65535); + fprintf(stderr, "\n===== BSS =====\n"); + my_safe_print_str(b_bss, 65535); + fprintf(stderr, "\n===== data =====\n"); + my_safe_print_str("LEGAL", 65535); + fprintf(stderr, "\n===== Above is a junk, but it is expected. =====\n"); +#endif /*__SANITIZE_ADDRESS__*/ + fprintf(stderr, "\n===== Nornal length test =====\n"); + my_safe_print_str("LEGAL", 5); + fprintf(stderr, "\n===== NULL =====\n"); + my_safe_print_str(0, 5); +#ifndef __SANITIZE_ADDRESS__ + fprintf(stderr, "\n===== (const char*) 1 =====\n"); + my_safe_print_str((const char*)1, 5); +#endif /*__SANITIZE_ADDRESS__*/ +#endif /*HAVE_STACKTRACE*/ + + free(b_heap); + + ok(1, "test_my_safe_print_str"); +} + + +int main(int argc __attribute__((unused)), char **argv) +{ + MY_INIT(argv[0]); + plan(1); + + test_my_safe_print_str(); + + my_end(0); + return exit_status(); +} diff --git a/unittest/mysys/thr_template.c b/unittest/mysys/thr_template.c new file mode 100644 index 00000000..1e715484 --- /dev/null +++ b/unittest/mysys/thr_template.c @@ -0,0 +1,73 @@ +/* Copyright (c) 2006-2008 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <tap.h> + +volatile uint32 bad; +pthread_mutex_t mutex; + +void do_tests(); + +void test_concurrently(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t *threads= malloc(n * sizeof(pthread_t)); + int i; + ulonglong now= my_interval_timer(); + + assert(threads); + bad= 0; + + diag("Testing %s with %d threads, %d iterations... ", test, n, m); + for (i= 0; i < n; i++) + { + if (pthread_create(&threads[i], 0, handler, &m) != 0) + { + diag("Could not create thread"); + abort(); + } + } + + for (i= 0; i < n; i++) + pthread_join(threads[i], 0); + + now= my_interval_timer() - now; + free(threads); + ok(!bad, "tested %s in %g secs (%d)", test, ((double)now)/1e9, bad); +} + +int main(int argc __attribute__((unused)), char **argv) +{ + MY_INIT(argv[0]); + + if (argv[1] && *argv[1]) + DBUG_SET_INITIAL(argv[1]); + + pthread_mutex_init(&mutex, 0); + +#define CYCLES 30000 +#define THREADS 30 + + diag("N CPUs: %d", my_getncpus()); + + do_tests(); + + pthread_mutex_destroy(&mutex); + 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 00000000..82623157 --- /dev/null +++ b/unittest/mysys/waiting_threads-t.c @@ -0,0 +1,286 @@ +/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 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); + 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(13); + compile_time_assert(THREADS >= 4); + + DBUG_PRINT("wt", ("================= initialization ===================")); + + 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 00000000..189ea5a1 --- /dev/null +++ b/unittest/mytap/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2007 MySQL AB, 2010 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 +# 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-1335 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) +ADD_LIBRARY(mytap tap.c) diff --git a/unittest/mytap/Doxyfile b/unittest/mytap/Doxyfile new file mode 100644 index 00000000..1b1c82b4 --- /dev/null +++ b/unittest/mytap/Doxyfile @@ -0,0 +1,1156 @@ +# Doxyfile 1.3.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of source +# files, where putting all generated files in the same directory would otherwise +# cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is used +# as the annotated text. Otherwise, the brief description is used as-is. If left +# blank, the following values are used ("$name" is automatically replaced with the +# name of the entity): "The $name class" "The $name widget" "The $name file" +# "is" "provides" "specifies" "contains" "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories +# that contain documented source files. You may enter file names like +# "myfile.cpp" or directories like "/usr/src/myproject". Separate the +# files or directories with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like +# *.cpp and *.h) to filter out the source-files in the directories. If +# left blank the following patterns are tested: *.c *.cc *.cxx *.cpp +# *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp *.h++ +# *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not +# subdirectories should be searched for input files as well. Possible +# values are YES and NO. If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that +# should excluded from the INPUT source files. This way you can easily +# exclude a subdirectory from a directory tree whose root is specified +# with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are +# excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to +# exclude certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = e + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = *.c + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more +# wildcard patterns (like *.h and *.hpp) to filter out the +# header-files in the directories. If left blank, the patterns +# specified with FILE_PATTERNS will be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names +# that are defined before the preprocessor is started (similar to the +# -D option of gcc). The argument of the tag is a list of macros of +# the form: name or name=definition (no spaces). If the definition and +# the = are omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES +# then this tag can be used to specify a list of macro names that +# should be expanded. The macro definition that is found in the +# sources will be used. Use the PREDEFINED tag if you want to use a +# different macro definition. + +EXPAND_AS_DEFINED = __attribute__ + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are +# alone on a line, have an all uppercase name, and do not end with a +# semicolon. Such function macros are typically used for boiler-plate +# code, and will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superseded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/unittest/mytap/t/basic-t.c b/unittest/mytap/t/basic-t.c new file mode 100644 index 00000000..68783838 --- /dev/null +++ b/unittest/mytap/t/basic-t.c @@ -0,0 +1,34 @@ +/* + Copyright (c) 2006, 2010, Oracle and/or its affiliates + + 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-1335 USA */ + +#include "my_config.h" + +#include <stdlib.h> +#include "../tap.h" + +int main() { + plan(5); + ok(1 == 1, "testing basic functions"); + ok(2 == 2, " "); + ok1(3 == 3); + if (1 == 1) + skip(2, "Sensa fragoli"); + else { + ok(1 == 2, "Should not be run at all"); + ok(1, "This one neither"); + } + return exit_status(); +} diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c new file mode 100644 index 00000000..86edea11 --- /dev/null +++ b/unittest/mytap/tap.c @@ -0,0 +1,705 @@ +/* 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 + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA + + Library for providing TAP support for testing C and C++ was written + by Mats Kindahl <mats@mysql.com>. +*/ + +#include "tap.h" + +#include "my_global.h" + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#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 elsewhere 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 + + Internal functions and data structures for the MyTAP implementation. +*/ + +/** + Test data structure. + + Data structure containing all information about the test suite. + + @ingroup MyTAP_Internal + */ +static TEST_DATA g_test = { NO_PLAN, 0, 0, "" }; + +/** + Output stream for test report message. + + The macro is just a temporary solution. + + @ingroup MyTAP_Internal + */ +#define tapout stdout + +/** + Emit the beginning of a test line, that is: "(not) ok", test number, + and description. + + To emit the directive, use the emit_dir() function + + @ingroup MyTAP_Internal + + @see emit_dir + + @param pass 'true' if test passed, 'false' otherwise + @param fmt Description of test in printf() format. + @param ap Vararg list for the description string above. + */ +static void +vemit_tap(int pass, char const *fmt, va_list ap) +{ + fprintf(tapout, "%sok %d%s", + pass ? "" : "not ", + ++g_test.last, + (fmt && *fmt) ? " - " : ""); + if (fmt && *fmt) + vfprintf(tapout, fmt, ap); + fflush(tapout); +} + + +/** + Emit a TAP directive. + + TAP directives are comments after that have the form: + + @code + ok 1 # skip reason for skipping + not ok 2 # todo some text explaining what remains + @endcode + + @ingroup MyTAP_Internal + + @param dir Directive as a string + @param why Explanation string + */ +static void +emit_dir(const char *dir, const char *why) +{ + fprintf(tapout, " # %s %s", dir, why); + fflush(tapout); +} + + +/** + Emit a newline to the TAP output stream. + + @ingroup MyTAP_Internal + */ +static void +emit_endl() +{ + fprintf(tapout, "\n"); + fflush(tapout); +} + +static void +handle_core_signal(int signo) +{ + BAIL_OUT("Signal %d thrown\n", signo); +} + +void +BAIL_OUT(char const *fmt, ...) +{ + va_list ap; + 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); +} + + +void +diag(char const *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(tapout, "# "); + vfprintf(tapout, fmt, ap); + emit_endl(); + va_end(ap); +} + +typedef struct signal_entry { + int signo; + void (*handler)(int); +} 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 } +#ifdef SIGBUS + , { SIGBUS, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGXCPU, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGXFSZ, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGSYS, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGTRAP, handle_core_signal } +#endif +}; + +int skip_big_tests= 1; +ulong start_time= 0; + +void +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 + */ + + for (i= 0; i < sizeof(install_signal)/sizeof(*install_signal); ++i) + signal(install_signal[i].signo, install_signal[i].handler); + + g_test.plan= count; + switch (count) + { + case NO_PLAN: + break; + default: + if (count > 0) + { + fprintf(tapout, "1..%d\n", count); + fflush(tapout); + } + break; + } +} + + +void +skip_all(char const *reason, ...) +{ + va_list ap; + va_start(ap, reason); + fprintf(tapout, "1..0 # skip "); + vfprintf(tapout, reason, ap); + fflush(tapout); + va_end(ap); + exit(0); +} + +void +ok(int pass, char const *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + if (!pass && *g_test.todo == '\0') + ++g_test.failed; + + vemit_tap(pass, fmt, ap); + va_end(ap); + if (*g_test.todo != '\0') + emit_dir("todo", g_test.todo); + emit_endl(); +} + +void +ok1(int const pass) +{ + va_list ap; + + memset(&ap, 0, sizeof(ap)); + + if (!pass && *g_test.todo == '\0') + ++g_test.failed; + + vemit_tap(pass, NULL, ap); + + if (*g_test.todo != '\0') + emit_dir("todo", g_test.todo); + + emit_endl(); +} + +void +skip(int how_many, char const * const fmt, ...) +{ + char reason[80]; + if (fmt && *fmt) + { + va_list ap; + va_start(ap, fmt); + vsnprintf(reason, sizeof(reason), fmt, ap); + va_end(ap); + } + else + reason[0] = '\0'; + + while (how_many-- > 0) + { + va_list ap; + memset((char*) &ap, 0, sizeof(ap)); /* Keep compiler happy */ + vemit_tap(1, NULL, ap); + emit_dir("skip", reason); + emit_endl(); + } +} + + +void +todo_start(char const *message, ...) +{ + va_list ap; + va_start(ap, message); + vsnprintf(g_test.todo, sizeof(g_test.todo), message, ap); + va_end(ap); +} + +void +todo_end() +{ + *g_test.todo = '\0'; +} + +int exit_status() +{ + char buff[60]; + + /* + If there were no plan, we write one last instead. + */ + if (g_test.plan == NO_PLAN) + plan(g_test.last); + + if (g_test.plan != g_test.last) + { + diag("%d tests planned but%s %d executed", + g_test.plan, (g_test.plan > g_test.last ? " only" : ""), g_test.last); + return EXIT_FAILURE; + } + + if (g_test.failed > 0) + { + 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(_WIN32) +#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(_WIN32) + 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) (sec/(3600.0*24)); + sec-=3600.0*24*tmp; + buff+= sprintf(buff, "%ld %s", tmp, tmp > 1 ? " days " : " day "); + } + if (sec >= 3600.0) + { + tmp=(ulong) (sec/3600.0); + sec-=3600.0*tmp; + buff+= sprintf(buff, "%ld %s", tmp, tmp > 1 ? " hours " : " hour "); + } + if (sec >= 60.0) + { + tmp=(ulong) (sec/60.0); + sec-=60.0*tmp; + buff+= sprintf(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 + + @section IntroSec Introduction + + Unit tests are used to test individual components of a system. In + contrast, functional tests usually test the entire system. The + rationale is that each component should be correct if the system is + to be correct. Unit tests are usually small pieces of code that + tests an individual function, class, a module, or other unit of the + code. + + Observe that a correctly functioning system can be built from + "faulty" components. The problem with this approach is that as the + system evolves, the bugs surface in unexpected ways, making + maintenance harder. + + The advantages of using unit tests to test components of the system + are several: + + - The unit tests can make a more thorough testing than the + functional tests by testing correctness even for pathological use + (which shouldn't be present in the system). This increases the + overall robustness of the system and makes maintenance easier. + + - It is easier and faster to find problems with a malfunctioning + component than to find problems in a malfunctioning system. This + shortens the compile-run-edit cycle and therefore improves the + overall performance of development. + + - The component has to support at least two uses: in the system and + in a unit test. This leads to more generic and stable interfaces + and in addition promotes the development of reusable components. + + For example, the following are typical functional tests: + - Does transactions work according to specifications? + - Can we connect a client to the server and execute statements? + + In contrast, the following are typical unit tests: + + - Can the 'String' class handle a specified list of character sets? + - Does all operations for 'my_bitmap' produce the correct result? + - Does all the NIST test vectors for the AES implementation encrypt + correctly? + + + @section UnitTest Writing unit tests + + The purpose of writing unit tests is to use them to drive component + development towards a solution that passes the tests. This means that the + unit tests has to be as complete as possible, testing at least: + + - Normal input + - Borderline cases + - Faulty input + - Error handling + - Bad environment + + @subsection NormalSubSec Normal input + + This is to test that the component have the expected behaviour. + This is just plain simple: test that it works. For example, test + that you can unpack what you packed, adding gives the sum, pincing + the duck makes it quack. + + This is what everybody does when they write tests. + + + @subsection BorderlineTests Borderline cases + + If you have a size anywhere for your component, does it work for + size 1? Size 0? Sizes close to <code>UINT_MAX</code>? + + It might not be sensible to have a size 0, so in this case it is + not a borderline case, but rather a faulty input (see @ref + FaultyInputTests). + + + @subsection FaultyInputTests Faulty input + + Does your bitmap handle 0 bits size? Well, it might not be designed + for it, but is should <em>not</em> crash the application, but + rather produce an error. This is called defensive programming. + + Unfortunately, adding checks for values that should just not be + entered at all is not always practical: the checks cost cycles and + might cost more than it's worth. For example, some functions are + designed so that you may not give it a null pointer. In those + cases it's not sensible to pass it <code>NULL</code> just to see it + crash. + + Since every experienced programmer add an <code>assert()</code> to + ensure that you get a proper failure for the debug builds when a + null pointer passed (you add asserts too, right?), you will in this + case instead have a controlled (early) crash in the debug build. + + + @subsection ErrorHandlingTests Error handling + + This is testing that the errors your component is designed to give + actually are produced. For example, testing that trying to open a + non-existing file produces a sensible error code. + + + @subsection BadEnvironmentTests Environment + + Sometimes, modules has to behave well even when the environment + fails to work correctly. Typical examples are when the computer is + out of dynamic memory or when the disk is full. You can emulate + this by replacing, e.g., <code>malloc()</code> with your own + version that will work for a while, but then fail. Some things are + worth to keep in mind here: + + - Make sure to make the function fail deterministically, so that + you really can repeat the test. + + - Make sure that it doesn't just fail immediately. The unit might + have checks for the first case, but might actually fail some time + in the near future. + + + @section UnitTest How to structure a unit test + + In this section we will give some advice on how to structure the + unit tests to make the development run smoothly. The basic + structure of a test is: + + - Plan + - Test + - Report + + + @subsection TestPlanning Plan the test + + Planning the test means telling how many tests there are. In the + event that one of the tests causes a crash, it is then possible to + see that there are fewer tests than expected, and print a proper + error message. + + To plan a test, use the @c plan() function in the following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + . + . + . + } + @endcode + + If you don't call the @c plan() function, the number of tests + executed will be printed at the end. This is intended to be used + while developing the unit and you are constantly adding tests. It + is not indented to be used after the unit has been released. + + + @subsection TestRunning Execute the test + + To report the status of a test, the @c ok() function is used in the + following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + ok(ducks == paddling_ducks, + "%d ducks did not paddle", ducks - paddling_ducks); + . + . + . + } + @endcode + + This will print a test result line on the standard output in TAP + format, which allows TAP handling frameworks (like Test::Harness) + to parse the status of the test. + + @subsection TestReport Report the result of the test + + At the end, a complete test report should be written, with some + statistics. If the test returns EXIT_SUCCESS, all tests were + successfull, otherwise at least one test failed. + + To get a TAP complient output and exit status, report the exit + status in the following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + ok(ducks == paddling_ducks, + "%d ducks did not paddle", ducks - paddling_ducks); + . + . + . + return exit_status(); + } + @endcode + + @section DontDoThis Ways to not do unit testing + + In this section, we'll go through some quite common ways to write + tests that are <em>not</em> a good idea. + + @subsection BreadthFirstTests Doing breadth-first testing + + If you're writing a library with several functions, don't test all + functions using size 1, then all functions using size 2, etc. If a + test for size 42 fails, you have no easy way of tracking down why + it failed. + + It is better to concentrate on getting one function to work at a + time, which means that you test each function for all sizes that + you think is reasonable. Then you continue with the next function, + doing the same. This is usually also the way that a library is + developed (one function at a time) so stick to testing that is + appropriate for now the unit is developed. + + @subsection JustToBeSafeTest Writing unnecessarily large tests + + Don't write tests that use parameters in the range 1-1024 unless + you have a very good reason to belive that the component will + succeed for 562 but fail for 564 (the numbers picked are just + examples). + + It is very common to write extensive tests "just to be safe." + Having a test suite with a lot of values might give you a warm + fuzzy feeling, but it doesn't really help you find the bugs. Good + tests fail; seriously, if you write a test that you expect to + succeed, you don't need to write it. If you think that it + <em>might</em> fail, <em>then</em> you should write it. + + Don't take this as an excuse to avoid writing any tests at all + "since I make no mistakes" (when it comes to this, there are two + kinds of people: those who admit they make mistakes, and those who + don't); rather, this means that there is no reason to test that + using a buffer with size 100 works when you have a test for buffer + size 96. + + The drawback is that the test suite takes longer to run, for little + or no benefit. It is acceptable to do a exhaustive test if it + doesn't take too long to run and it is quite common to do an + exhaustive test of a function for a small set of values. + Use your judgment to decide what is excessive: your milage may + vary. +*/ + +/** + @example simple.t.c + + This is an simple example of how to write a test using the + library. The output of this program is: + + @code + 1..1 + # Testing basic functions + ok 1 - Testing gcs() + @endcode + + The basic structure is: plan the number of test points using the + plan() function, perform the test and write out the result of each + test point using the ok() function, print out a diagnostics message + using diag(), and report the result of the test by calling the + exit_status() function. Observe that this test does excessive + testing (see @ref JustToBeSafeTest), but the test point doesn't + take very long time. +*/ + +/** + @example todo.t.c + + This example demonstrates how to use the <code>todo_start()</code> + and <code>todo_end()</code> function to mark a sequence of tests to + be done. Observe that the tests are assumed to fail: if any test + succeeds, it is considered a "bonus". +*/ + +/** + @example skip.t.c + + This is an example of how the <code>SKIP_BLOCK_IF</code> can be + used to skip a predetermined number of tests. Observe that the + macro actually skips the following statement, but it's not sensible + to use anything than a block. +*/ + +/** + @example skip_all.t.c + + Sometimes, you skip an entire test because it's testing a feature + that doesn't exist on the system that you're testing. To skip an + entire test, use the <code>skip_all()</code> function according to + this example. + */ diff --git a/unittest/mytap/tap.h b/unittest/mytap/tap.h new file mode 100644 index 00000000..9b79b771 --- /dev/null +++ b/unittest/mytap/tap.h @@ -0,0 +1,314 @@ +/* Copyright (c) 2006, 2010, Oracle and/or its affiliates + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA + + Library for providing TAP support for testing C and C++ was written + by Mats Kindahl <mats@mysql.com>. +*/ + +#ifndef TAP_H +#define TAP_H + +#include "my_global.h" + +/* + @defgroup MyTAP MySQL support for performing unit tests according to + the Test Anything Protocol (TAP). +*/ + +#define NO_PLAN (0) + +/** + Data about test plan. + + @ingroup MyTAP_Internal + + @internal We are using the "typedef struct X { ... } X" idiom to + create class/struct X both in C and C++. + */ + +typedef struct TEST_DATA { + /** + Number of tests that is planned to execute. + + Can be zero (<code>NO_PLAN</code>) meaning that the plan string + will be printed at the end of test instead. + */ + int plan; + + /** Number of last test that was done or skipped. */ + int last; + + /** Number of tests that failed. */ + int failed; + + /** Todo reason. */ + char todo[128]; +} TEST_DATA; + +#ifdef __cplusplus +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. + + @{ +*/ + +/** + Set number of tests that is planned to execute. + + The function also accepts the predefined constant <code>NO_PLAN</code>. + If invoked with this constant -- or not invoked at all -- + the test plan will be printed after all the test lines. + + The plan() function will install signal handlers for all signals + that generate a core, so if you want to override these signals, do + it <em>after</em> you have called the plan() function. + + 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 count); + + +/** + Report test result as a TAP line. + + Function used to write status of an individual test. Call this + function in the following manner: + + @code + ok(ducks == paddling, + "%d ducks did not paddle", ducks - paddling); + @endcode + + @param pass Zero if the test failed, non-zero if it passed. + @param fmt Format string in printf() format. NULL is not allowed, + use ok1() in this case. +*/ + +void ok(int pass, char const *fmt, ...) + __attribute__((format(printf,2,3))); + + +/** + Report test result as a TAP line. + + Same as ok() but does not take a message to be printed. + + @param pass Zero if the test failed, non-zero if it passed. +*/ + +void ok1(int const pass); + + +/** + Skip a determined number of tests. + + Function to print that <em>how_many</em> tests have been skipped. + The reason is printed for each skipped test. Observe that this + function does not do the actual skipping for you, it just prints + information that tests have been skipped. This function is not + usually used, but rather the macro @c SKIP_BLOCK_IF, which does the + skipping for you. + + It shall be used in the following manner: + + @code + if (ducks == 0) { + skip(2, "No ducks in the pond"); + } else { + int i; + for (i = 0 ; i < 2 ; ++i) + ok(duck[i] == paddling, "is duck %d paddling?", i); + } + @endcode + + @see SKIP_BLOCK_IF + + @param how_many Number of tests that are to be skipped. + @param reason A reason for skipping the tests + */ + +void skip(int how_many, char const *const reason, ...) + __attribute__((format(printf,2,3))); + + +/** + Helper macro to skip a block of code. The macro can be used to + simplify conditionally skipping a block of code. It is used in the + following manner: + + @code + SKIP_BLOCK_IF(ducks == 0, 2, "No ducks in the pond") + { + int i; + for (i = 0 ; i < 2 ; ++i) + ok(duck[i] == paddling, "is duck %d paddling?", i); + } + @endcode + + @see skip + */ + +#define SKIP_BLOCK_IF(SKIP_IF_TRUE, COUNT, REASON) \ + if (SKIP_IF_TRUE) skip((COUNT),(REASON)); else + + +/** + 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. + */ + +void diag(char const *fmt, ...) + __attribute__((format(printf,1,2))); + + +/** + Print a bail out message. + + A bail out message can be issued when no further testing can be + done, e.g., when there are missing dependencies. + + The test will exit with status 255. This function does not return. + + @code + BAIL_OUT("Lost connection to server %s", server_name); + @endcode + + @note A bail out message is printed if a signal that generates a + core is raised. + + @param fmt Bail out message in printf() format. +*/ + +void BAIL_OUT(char const *fmt, ...) + __attribute__((noreturn, format(printf,1,2))); + + +/** + Print summary report and return exit status. + + This function will print a summary report of how many tests passed, + how many were skipped, and how many remains to do. The function + should be called after all tests are executed in the following + manner: + + @code + return exit_status(); + @endcode + + @returns @c EXIT_SUCCESS if all tests passed, @c EXIT_FAILURE if + one or more tests failed. + */ + +int exit_status(void); + + +/** + Skip entire test suite. + + To skip the entire test suite, use this function. It will + automatically call exit(), so there is no need to have checks + around it. + */ + +void skip_all(char const *reason, ...) + __attribute__((noreturn, format(printf, 1, 2))); + + +/** + Start section of tests that are not yet ready. + + To start a section of tests that are not ready and are expected to + fail, use this function and todo_end() in the following manner: + + @code + todo_start("Not ready yet"); + ok(is_rocketeering(duck), "Rocket-propelled ducks"); + ok(is_kamikaze(duck), "Kamikaze ducks"); + todo_end(); + @endcode + + @see todo_end + + @note + It is not possible to nest todo sections. + + @param message Message that will be printed before the todo tests. +*/ + +void todo_start(char const *message, ...) + __attribute__((format(printf, 1, 2))); + + +/** + End a section of tests that are not yet ready. +*/ + +void todo_end(); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* TAP_H */ diff --git a/unittest/sql/CMakeLists.txt b/unittest/sql/CMakeLists.txt new file mode 100644 index 00000000..b8682de7 --- /dev/null +++ b/unittest/sql/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) 2006, 2013, 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 +# 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-1335 USA + +MY_ADD_TESTS(my_apc LINK_LIBRARIES mysys EXT cc) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/unittest/mytap + ${CMAKE_SOURCE_DIR}/extra/yassl/include) +ADD_EXECUTABLE(explain_filename-t explain_filename-t.cc dummy_builtins.cc) + + +TARGET_LINK_LIBRARIES(explain_filename-t sql mytap) +MY_ADD_TEST(explain_filename) + +ADD_EXECUTABLE(mf_iocache-t mf_iocache-t.cc ../../sql/mf_iocache_encr.cc) +TARGET_LINK_LIBRARIES(mf_iocache-t mysys mytap mysys_ssl) +ADD_DEPENDENCIES(mf_iocache-t GenError) +MY_ADD_TEST(mf_iocache) + +# Json writer needs String which needs sql library +ADD_EXECUTABLE(my_json_writer-t my_json_writer-t.cc dummy_builtins.cc) +TARGET_LINK_LIBRARIES(my_json_writer-t sql mytap) +MY_ADD_TEST(my_json_writer) diff --git a/unittest/sql/dummy_builtins.cc b/unittest/sql/dummy_builtins.cc new file mode 100644 index 00000000..31d043d1 --- /dev/null +++ b/unittest/sql/dummy_builtins.cc @@ -0,0 +1,26 @@ +/* 2018 MariaDB + + 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-1335 USA */ + +#include <my_global.h> +#include <mysql/plugin.h> +struct st_maria_plugin *mysql_optional_plugins[]= +{ + 0 +}; + +struct st_maria_plugin *mysql_mandatory_plugins[]= +{ + 0 +}; diff --git a/unittest/sql/explain_filename-t.cc b/unittest/sql/explain_filename-t.cc new file mode 100644 index 00000000..32291d5e --- /dev/null +++ b/unittest/sql/explain_filename-t.cc @@ -0,0 +1,166 @@ +/* + Copyright (c) 2000, 2013, 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 + 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-1335 USA +*/ + +/** Unit test case for the function explain_filename(). */ + +#include <tap.h> +#include <mysqld_error.h> +#include <sql_class.h> +#include <sql_table.h> + +#define BUFLEN 1000 +char to[BUFLEN]; +char from[BUFLEN]; + +static const char *error_messages_txt[1000]; +static const char **error_messages[1]= { error_messages_txt }; + +int setup() +{ + system_charset_info = &my_charset_utf8mb3_bin; + my_default_lc_messages = &my_locale_en_US; + + /* Populate the necessary error messages */ + error_messages[0][ER_DATABASE_NAME - ER_ERROR_FIRST] = "Database"; + error_messages[0][ER_TABLE_NAME - ER_ERROR_FIRST] = "Table"; + error_messages[0][ER_PARTITION_NAME - ER_ERROR_FIRST] = "Partition"; + error_messages[0][ER_SUBPARTITION_NAME - ER_ERROR_FIRST] = "Subpartition"; + error_messages[0][ER_TEMPORARY_NAME - ER_ERROR_FIRST] = "Temporary"; + error_messages[0][ER_RENAMED_NAME - ER_ERROR_FIRST] = "Renamed"; + + my_default_lc_messages->errmsgs->errmsgs = error_messages; + + return 0; +} + +void test_1(const char *in, const char *exp, enum_explain_filename_mode mode) +{ + char out[BUFLEN]; + + uint len1 = explain_filename(0, in, out, BUFLEN, mode); + + /* expected output and actual output must be same */ + bool pass = (strcmp(exp, out) == 0); + + /* length returned by explain_filename is fine */ + bool length = (len1 == strlen(exp)); + + ok( (pass && length) , "(%d): %s => %s", mode, in, out); +} + +int main(int argc __attribute__((unused)),char *argv[]) +{ + MY_INIT(argv[0]); + setup(); + plan(22); + + test_1("test/t1.ibd", + "Database `test`, Table `t1.ibd`", + EXPLAIN_ALL_VERBOSE); + + test_1("test/t1.ibd", + "`test`.`t1.ibd`", + EXPLAIN_PARTITIONS_VERBOSE); + + test_1("test/t1.ibd", + "`test`.`t1.ibd`", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/t1#TMP#", + "Database `test`, Table `t1#TMP#`", + EXPLAIN_ALL_VERBOSE); + + test_1("test/#sql-2882.ibd", + "Database `test`, Table `#sql-2882.ibd`", + EXPLAIN_ALL_VERBOSE); + + test_1("test/t1#REN#", + "Database `test`, Table `t1#REN#`", + EXPLAIN_ALL_VERBOSE); + + test_1("test/t1@0023REN@0023", + "Database `test`, Table `t1#REN#`", + EXPLAIN_ALL_VERBOSE); + + test_1("test/t1#p#p1", + "Database `test`, Table `t1`, Partition `p1`", + EXPLAIN_ALL_VERBOSE); + + test_1("test/t1#P#p1", + "`test`.`t1` /* Partition `p1` */", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/t1#P#p1@00231", + "`test`.`t1` /* Partition `p1#1` */", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/t1#P#p1#SP#sp1", + "`test`.`t1` /* Partition `p1`, Subpartition `sp1` */", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/t1#p1#SP#sp1", + "`test`.`t1#p1#SP#sp1`", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/t1#p#p1@00232#SP#sp1@00231#REN#", + "`test`.`t1` /* Renamed Partition `p1#2`, Subpartition `sp1#1` */", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/t1#p#p1#SP#sp1#TMP#", + "`test`.`t1` /* Temporary Partition `p1`, Subpartition `sp1` */", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/#sql-t1#P#p1#SP#sp1#TMP#", + "`test`.`#sql-t1#P#p1#SP#sp1#TMP#` /* Temporary Partition `p1`, Subpartition `sp1` */", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/#sql-t1#P#p1#SP#sp1", + "`test`.`#sql-t1#P#p1#SP#sp1` /* Partition `p1`, Subpartition `sp1` */", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/#sqlx-33", + "`test`.`#sqlx-33`", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/#mysql50#t", + "`test`.`#mysql50#t`", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("#mysql50#t", + "`#mysql50#t`", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("@0023t", + "`#t`", + EXPLAIN_PARTITIONS_AS_COMMENT); + + test_1("test/t@0023", + "`test`.`t#`", + EXPLAIN_PARTITIONS_AS_COMMENT); + + /* + If a character not allowed in my_charset_filename is encountered, + then it will not be converted to system_charset_info! + */ + test_1("test/t@0023#", + "`test`.`t@0023#`", + EXPLAIN_PARTITIONS_AS_COMMENT); + + my_end(0); + return exit_status(); +} + diff --git a/unittest/sql/mf_iocache-t.cc b/unittest/sql/mf_iocache-t.cc new file mode 100644 index 00000000..cc97d3b2 --- /dev/null +++ b/unittest/sql/mf_iocache-t.cc @@ -0,0 +1,474 @@ +/* Copyright (C) 2015 MariaDB + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <my_crypt.h> +#include <tap.h> + +/*** tweaks and stubs for encryption code to compile ***************/ +#define KEY_SIZE (128/8) + +my_bool encrypt_tmp_files; +int init_io_cache_encryption(); + +uint encryption_key_get_latest_version_func(uint) +{ + return 1; +} + +uint encryption_key_id_exists_func(uint) +{ + return 1; +} + +uint encryption_key_version_exists_func(uint, uint) +{ + return 1; +} + +uint encryption_key_get_func(uint, uint, uchar* key, uint* size) +{ + if (*size < KEY_SIZE) + { + *size= KEY_SIZE; + return ENCRYPTION_KEY_BUFFER_TOO_SMALL; + } + memset(key, KEY_SIZE, *size= KEY_SIZE); + return 0; +} + +uint encryption_ctx_size_func(unsigned int, unsigned int) +{ + return MY_AES_CTX_SIZE; +} + + +#ifdef HAVE_EncryptAes128Gcm +enum my_aes_mode aes_mode= MY_AES_GCM; +#else +enum my_aes_mode aes_mode= MY_AES_CBC; +#endif + +int encryption_ctx_init_func(void *ctx, const unsigned char* key, unsigned int klen, + const unsigned char* iv, unsigned int ivlen, + int flags, unsigned int key_id, + unsigned int key_version) +{ + return my_aes_crypt_init(ctx, aes_mode, flags, key, klen, iv, ivlen); +} + +uint encryption_encrypted_length_func(unsigned int slen, unsigned int key_id, unsigned int key_version) +{ + return my_aes_get_size(aes_mode, slen); +} + +struct encryption_service_st encryption_handler= +{ + encryption_key_get_latest_version_func, + encryption_key_get_func, + encryption_ctx_size_func, + encryption_ctx_init_func, + my_aes_crypt_update, + my_aes_crypt_finish, + encryption_encrypted_length_func +}; + +void sql_print_information(const char *format, ...) +{ +} + +void sql_print_error(const char *format, ...) +{ +} + +/*** end of encryption tweaks and stubs ****************************/ + +static IO_CACHE info; +#define CACHE_SIZE 16384 + +#define INFO_TAIL ", pos_in_file = %llu, pos_in_mem = %lu", \ + info.pos_in_file, (ulong) ((info.type == READ_CACHE ? info.read_pos : info.write_pos) - info.request_pos) + +#define FILL 0x5A + +int data_bad(const uchar *buf, size_t len) +{ + const uchar *end= buf + len; + while (buf < end) + if (*buf++ != FILL) + return 1; + return 0; +} + +void temp_io_cache() +{ + int res; + uchar buf[CACHE_SIZE + 200]; + memset(buf, FILL, sizeof(buf)); + + diag("temp io_cache with%s encryption", encrypt_tmp_files?"":"out"); + + init_io_cache_encryption(); + + res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0); + ok(res == 0, "open_cached_file" INFO_TAIL); + + res= my_b_write(&info, buf, 100); + ok(res == 0 && info.pos_in_file == 0, "small write" INFO_TAIL ); + + res= my_b_write(&info, buf, sizeof(buf)); + ok(res == 0 && info.pos_in_file == CACHE_SIZE, "large write" INFO_TAIL); + + res= reinit_io_cache(&info, WRITE_CACHE, 250, 0, 0); + ok(res == 0, "reinit with rewind" INFO_TAIL); + + res= my_b_write(&info, buf, sizeof(buf)); + ok(res == 0, "large write" INFO_TAIL); + + res= my_b_flush_io_cache(&info, 1); + ok(res == 0, "flush" INFO_TAIL); + + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + res= (int)my_pread(info.file, buf, 50, 50, MYF(MY_NABP)); + ok(res == 0 && data_bad(buf, 50) == encrypt_tmp_files, + "file must be %sreadable", encrypt_tmp_files ?"un":""); + + res= my_b_read(&info, buf, 50) || data_bad(buf, 50); + ok(res == 0 && info.pos_in_file == 0, "small read" INFO_TAIL); + + res= my_b_read(&info, buf, sizeof(buf)) || data_bad(buf, sizeof(buf)); + ok(res == 0 && info.pos_in_file == CACHE_SIZE, "large read" INFO_TAIL); + + close_cached_file(&info); +} + +void mdev9044() +{ + int res; + uchar buf[CACHE_SIZE + 200]; + + diag("MDEV-9044 Binlog corruption in Galera"); + + res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0); + ok(res == 0, "open_cached_file" INFO_TAIL); + + res= my_b_write(&info, USTRING_WITH_LEN("first write\0")); + ok(res == 0, "first write" INFO_TAIL); + + res= my_b_flush_io_cache(&info, 1); + ok(res == 0, "flush" INFO_TAIL); + + res= reinit_io_cache(&info, WRITE_CACHE, 0, 0, 0); + ok(res == 0, "reinit WRITE_CACHE" INFO_TAIL); + + res= my_b_write(&info, USTRING_WITH_LEN("second write\0")); + ok(res == 0, "second write" INFO_TAIL ); + + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + res= (int)my_b_fill(&info); + ok(res == 0, "fill" INFO_TAIL); + + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + res= my_b_read(&info, buf, sizeof(buf)); + ok(res == 1 && strcmp((char*)buf, "second write") == 0, "read '%s'", buf); + + close_cached_file(&info); +} + +/* 2 Reads (with my_b_fill) in cache makes second read to fail */ +void mdev10259() +{ + int res; + uchar buf[200]; + memset(buf, FILL, sizeof(buf)); + + diag("MDEV-10259- mysqld crash with certain statement length and order with" + " Galera and encrypt-tmp-files=1"); + + init_io_cache_encryption(); + + res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0); + ok(res == 0, "open_cached_file" INFO_TAIL); + + res= my_b_write(&info, buf, sizeof(buf)); + ok(res == 0 && info.pos_in_file == 0, "200 write" INFO_TAIL); + + res= my_b_flush_io_cache(&info, 1); + ok(res == 0, "flush" INFO_TAIL); + + my_off_t saved_pos= my_b_tell(&info); + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + size_t s= my_b_fill(&info); + ok(s == 200, "fill" INFO_TAIL); + + s= my_b_fill(&info); + ok(s == 0, "fill" INFO_TAIL); + + s= my_b_fill(&info); + ok(s == 0, "fill" INFO_TAIL); + + res= reinit_io_cache(&info, WRITE_CACHE, saved_pos, 0, 0); + ok(res == 0, "reinit WRITE_CACHE" INFO_TAIL); + + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + ok(200 == my_b_bytes_in_cache(&info),"my_b_bytes_in_cache == 200"); + + s= my_b_fill(&info); + ok(s == 0, "fill" INFO_TAIL); + + s= my_b_fill(&info); + ok(s == 0, "fill" INFO_TAIL); + + s= my_b_fill(&info); + ok(s == 0, "fill" INFO_TAIL); + + res= reinit_io_cache(&info, WRITE_CACHE, saved_pos, 0, 0); + ok(res == 0, "reinit WRITE_CACHE" INFO_TAIL); + + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + ok(200 == my_b_bytes_in_cache(&info),"my_b_bytes_in_cache == 200"); + + res= my_b_read(&info, buf, sizeof(buf)) || data_bad(buf, sizeof(buf)); + ok(res == 0 && info.pos_in_file == 0, "large read" INFO_TAIL); + + close_cached_file(&info); + +} + +void mdev14014() +{ + int res; + uchar buf_o[200]; + uchar buf_i[200]; + memset(buf_i, 0, sizeof( buf_i)); + memset(buf_o, FILL, sizeof(buf_o)); + + diag("MDEV-14014 Dump thread reads past last 'officially' written byte"); + + init_io_cache_encryption(); + + res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0); + ok(res == 0, "open_cached_file" INFO_TAIL); + + res= my_b_write(&info, buf_o, sizeof(buf_o)); + ok(res == 0, "buffer is written" INFO_TAIL); + + res= my_b_flush_io_cache(&info, 1); + ok(res == 0, "flush" INFO_TAIL); + + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + info.end_of_file= 100; + res= my_b_read(&info, buf_i, sizeof(buf_i)); + ok(res == 1 && buf_i[100] == 0 && buf_i[200-1] == 0, + "short read leaves buf_i[100..200-1] == 0"); + + close_cached_file(&info); +} + +void mdev17133() +{ + my_off_t res; + int k; + const int eof_iter=4, read_iter= 4; + uchar buf_i[1024*256]; // read + uchar buf_o[sizeof(buf_i)]; // write + const size_t eof_block_size= sizeof(buf_o) / eof_iter; + const size_t read_size= eof_block_size / read_iter; + size_t total; + + srand((uint) time(NULL)); + memset(buf_i, 0, sizeof( buf_i)); + memset(buf_o, FILL, sizeof(buf_o)); + + diag("MDEV-17133 Dump thread reads from the past"); + + init_io_cache_encryption(); + + res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0); + ok(res == 0, "open_cached_file" INFO_TAIL); + + res= my_b_write(&info, buf_o, sizeof(buf_o)); + ok(res == 0, "buffer is written" INFO_TAIL); + res= my_b_tell(&info); + ok(res == sizeof(buf_o), "cache size as expected"); + + res= my_b_flush_io_cache(&info, 1); + ok(res == 0, "flush" INFO_TAIL); + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + // read the written data by chunks of variable size eof_iter times + for (k= eof_iter, info.end_of_file=0, total= 0; k; k--) + { + int i; + size_t curr_read_size; + info.end_of_file= + k == 1 ? sizeof(buf_o) : + MY_MIN(sizeof(buf_o), + info.end_of_file + eof_block_size + + // plus 25% of block for randomization to the average + eof_block_size/4 - rand() % (eof_block_size/2)); + + // read a chunk by blocks of variable size read_iter times + // the last block completes the current chunk + for (i= 0; i < read_iter; i++, total += curr_read_size) + { + char buf_check[eof_block_size]; + size_t a,b; + + a= (size_t)(info.end_of_file - total); + b= read_size + read_size/4 - rand() % (read_size/2); + curr_read_size= (i == read_iter - 1) ? a : + MY_MIN(a, b); + + DBUG_ASSERT(curr_read_size <= info.end_of_file - total); + + res= my_b_read(&info, buf_i + total, MY_MIN(19, curr_read_size)); + ok(res == 0, "read of 19"); + // mark read bytes in the used part of the cache buffer + memset(info.buffer, 0, info.read_pos - info.buffer); + + // random size 2nd read + res= my_b_read(&info, buf_i + total + MY_MIN(19, curr_read_size), + 19 >= curr_read_size ? 0 : curr_read_size - 19); + ok(res == 0, "rest of read %zu", curr_read_size - 19); + // mark read bytes in the used part of the cache buffer + memset(info.buffer, 0, info.read_pos - info.buffer); + + // check that no marked bytes are read + memset(buf_check, FILL, curr_read_size); + ok(memcmp(buf_i + total, buf_check, curr_read_size) == 0, + "read correct data"); + } + ok(info.pos_in_file + (info.read_end - info.buffer) == info.end_of_file, + "cache is read up to eof"); + ok(total == info.end_of_file, "total matches eof"); + } + ok(total == sizeof(buf_i), "read total size match"); + ok(buf_i[sizeof(buf_i) - 1] == FILL, "data read correctly"); + + close_cached_file(&info); +} + + +void mdev10963() +{ + int res; + uint n_checks= 8; + uchar buf[1024 * 512]; + uint n_frag= sizeof(buf)/(2 * CACHE_SIZE); + FILE *file; + myf my_flags= MYF(MY_WME); + const char *file_name="cache.log"; + + memset(buf, FILL, sizeof(buf)); + diag("MDEV-10963 Fragmented BINLOG query"); + + init_io_cache_encryption(); + srand((uint) time(NULL)); + + /* copying source */ + res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0); + ok(res == 0, "open_cached_file" INFO_TAIL); + res= my_b_write(&info, buf, sizeof(buf)); + + ulonglong total_size= my_b_tell(&info); + ok(res == 0 && total_size == sizeof(buf), "cache is written"); + + /* destination */ + file= my_fopen(file_name, O_RDWR | O_TRUNC | O_CREAT, my_flags); + ok(my_fileno(file) > 0, "opened file fd = %d", my_fileno(file)); + + /* + For n_checks times verify a sequence of copying with random fragment + size ranging from zero to about the double of the cache read buffer size. + */ + for (; n_checks; n_checks--, rewind(file)) + { + // copied size is an estimate can be incremeneted to greater than total_size + ulong copied_size= 0; + + res= reinit_io_cache(&info, READ_CACHE, 0L, FALSE, FALSE); + ok(res == 0, "cache turned to read"); + + for (ulong i= 0, curr_size= 0; i < n_frag; i++, copied_size += curr_size) + { + curr_size= rand() % (2 * (total_size - copied_size) / (n_frag - i)); + + DBUG_ASSERT(curr_size <= total_size - copied_size || i == n_frag - 1); + + res= my_b_copy_to_file(&info, file, curr_size); + ok(res == 0, "%lu of the cache copied to file", curr_size); + } + /* + Regardless of total_size <> copied_size the function succeeds: + when total_size < copied_size the huge overflowed value of the last + argument is ignored because nothing already left uncopied in the cache. + */ + res= my_b_copy_to_file(&info, file, (size_t) total_size - copied_size); + ok(res == 0, "%llu of the cache copied to file", total_size - copied_size); + ok(my_ftell(file, my_flags) == sizeof(buf), + "file written in %d fragments", n_frag+1); + + res= reinit_io_cache(&info, WRITE_CACHE, total_size, 0, 0); + ok(res == 0 && my_b_tell(&info) == sizeof(buf), "cache turned to write"); + } + close_cached_file(&info); + my_fclose(file, my_flags); + my_delete(file_name, MYF(MY_WME)); +} + +int main(int argc __attribute__((unused)),char *argv[]) +{ + MY_INIT(argv[0]); + plan(277); + + /* temp files with and without encryption */ + encrypt_tmp_files= 1; + temp_io_cache(); + + encrypt_tmp_files= 0; + temp_io_cache(); + + /* regression tests */ + mdev9044(); + + encrypt_tmp_files= 1; + mdev10259(); + encrypt_tmp_files= 0; + + mdev14014(); + mdev17133(); + mdev10963(); + + my_end(0); + return exit_status(); +} + diff --git a/unittest/sql/my_apc-t.cc b/unittest/sql/my_apc-t.cc new file mode 100644 index 00000000..c08e7281 --- /dev/null +++ b/unittest/sql/my_apc-t.cc @@ -0,0 +1,229 @@ +/* + Copyright (c) 2012, 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 + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + This file does standalone APC system tests. +*/ +#include <my_global.h> +#include <my_pthread.h> +#include <my_sys.h> + +#include <stdio.h> + +#include <tap.h> + +/* + A fake THD with enter_cond/exit_cond and some other members. +*/ +PSI_stage_info stage_show_explain; +class THD +{ + mysql_mutex_t* thd_mutex; +public: + bool killed; + + THD() : killed(FALSE) {} + inline const char* ENTER_COND(mysql_cond_t *cond, mysql_mutex_t* mutex, + PSI_stage_info*, PSI_stage_info*) + { + mysql_mutex_assert_owner(mutex); + thd_mutex= mutex; + return NULL; + } + inline void EXIT_COND(PSI_stage_info*) + { + mysql_mutex_unlock(thd_mutex); + } +}; + +#include "../sql/my_apc.h" + +#define MY_APC_STANDALONE 1 +#include "../sql/my_apc.cc" + +volatile bool started= FALSE; +volatile bool service_should_exit= FALSE; +volatile bool requestors_should_exit=FALSE; + +/* Counters for APC calls */ +int apcs_served= 0; +int apcs_missed=0; +int apcs_timed_out=0; +mysql_mutex_t apc_counters_mutex; + +inline void increment_counter(int *var) +{ + mysql_mutex_lock(&apc_counters_mutex); + *var= *var+1; + mysql_mutex_unlock(&apc_counters_mutex); +} + +volatile bool have_errors= false; + +Apc_target apc_target; +mysql_mutex_t target_mutex; + +int int_rand(int size) +{ + return (int) (0.5 + ((double)rand() / RAND_MAX) * size); +} + +/* + APC target thread (the one that will serve the APC requests). We will have + one target. +*/ +void *test_apc_service_thread(void *ptr) +{ + my_thread_init(); + mysql_mutex_init(0, &target_mutex, MY_MUTEX_INIT_FAST); + apc_target.init(&target_mutex); + apc_target.enable(); + started= TRUE; + diag("test_apc_service_thread started"); + while (!service_should_exit) + { + //apc_target.disable(); + my_sleep(10000); + //apc_target.enable(); + for (int i = 0; i < 10 && !service_should_exit; i++) + { + apc_target.process_apc_requests(); + my_sleep(int_rand(30)); + } + } + apc_target.disable(); + apc_target.destroy(); + mysql_mutex_destroy(&target_mutex); + my_thread_end(); + pthread_exit(0); + return NULL; +} + + +/* + One APC request (to write 'value' into *where_to) +*/ +class Apc_order : public Apc_target::Apc_call +{ +public: + int value; // The value + int *where_to; // Where to write it + Apc_order(int a, int *b) : value(a), where_to(b) {} + + void call_in_target_thread() + { + my_sleep(int_rand(1000)); + *where_to = value; + increment_counter(&apcs_served); + } +}; + + +/* + APC requestor thread. It makes APC requests, and checks if they were actually + executed. +*/ +void *test_apc_requestor_thread(void *ptr) +{ + my_thread_init(); + diag("test_apc_requestor_thread started"); + THD my_thd; + + while (!requestors_should_exit) + { + int dst_value= 0; + int src_value= int_rand(4*1000*100); + /* Create an APC to do "dst_value= src_value" assignment */ + Apc_order apc_order(src_value, &dst_value); + bool timed_out; + + mysql_mutex_lock(&target_mutex); + bool res= apc_target.make_apc_call(&my_thd, &apc_order, 60, &timed_out); + if (res) + { + if (timed_out) + increment_counter(&apcs_timed_out); + else + increment_counter(&apcs_missed); + + if (dst_value != 0) + { + diag("APC was done even though return value says it wasnt!"); + have_errors= true; + } + } + else + { + if (dst_value != src_value) + { + diag("APC was not done even though return value says it was!"); + have_errors= true; + } + } + //my_sleep(300); + } + diag("test_apc_requestor_thread exiting"); + my_thread_end(); + return NULL; +} + +/* Number of APC requestor threads */ +const int N_THREADS=23; + + +int main(int args, char **argv) +{ + pthread_t service_thr; + pthread_t request_thr[N_THREADS]; + int i; + + my_thread_global_init(); + + mysql_mutex_init(0, &apc_counters_mutex, MY_MUTEX_INIT_FAST); + + plan(1); + diag("Testing APC delivery and execution"); + + pthread_create(&service_thr, NULL, test_apc_service_thread, (void*)NULL); + while (!started) + my_sleep(1000); + for (i = 0; i < N_THREADS; i++) + pthread_create(&request_thr[i], NULL, test_apc_requestor_thread, (void*)NULL); + + for (i = 0; i < 15; i++) + { + my_sleep(500*1000); + diag("%d APCs served %d missed", apcs_served, apcs_missed); + } + diag("Shutting down requestors"); + requestors_should_exit= TRUE; + for (i = 0; i < N_THREADS; i++) + pthread_join(request_thr[i], NULL); + + diag("Shutting down service"); + service_should_exit= TRUE; + pthread_join(service_thr, NULL); + + mysql_mutex_destroy(&apc_counters_mutex); + + diag("Done"); + my_thread_end(); + my_thread_global_end(); + + ok1(!have_errors); + return exit_status(); +} + diff --git a/unittest/sql/my_json_writer-t.cc b/unittest/sql/my_json_writer-t.cc new file mode 100644 index 00000000..6398a589 --- /dev/null +++ b/unittest/sql/my_json_writer-t.cc @@ -0,0 +1,128 @@ +/* + Copyright (c) 2021, MariaDB Corporation. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_pthread.h> +#include <my_sys.h> +#include <stdio.h> +#include <tap.h> + +/* + Unit tests for class Json_writer. At the moment there are only tests for the + "Fail an assertion if one attempts to produce invalid JSON" feature. +*/ + +struct TABLE; +struct JOIN_TAB; +class Json_writer; + + +/* Several fake objects */ +class Opt_trace +{ +public: + void enable_tracing_if_required() {} + void disable_tracing_if_required() {} + Json_writer *get_current_json() { return nullptr; } +}; + +class THD +{ +public: + Opt_trace opt_trace; +}; + +#define JSON_WRITER_UNIT_TEST +#include "../sql/my_json_writer.h" +#include "../sql/my_json_writer.cc" + +int main(int args, char **argv) +{ + plan(NO_PLAN); + diag("Testing Json_writer checks"); + + { + Json_writer w; + w.start_object(); + w.add_member("foo"); + w.end_object(); + ok(w.invalid_json, "Started a name but didn't add a value"); + } + + { + Json_writer w; + w.start_object(); + w.add_ull(123); + ok(w.invalid_json, "Unnamed value in an object"); + } + + { + Json_writer w; + w.start_array(); + w.add_member("bebebe").add_ull(345); + ok(w.invalid_json, "Named member in array"); + } + + { + Json_writer w; + w.start_object(); + w.start_array(); + ok(w.invalid_json, "Unnamed array in an object"); + } + + { + Json_writer w; + w.start_object(); + w.start_object(); + ok(w.invalid_json, "Unnamed object in an object"); + } + + { + Json_writer w; + w.start_array(); + w.add_member("zzz"); + w.start_object(); + ok(w.invalid_json, "Named object in an array"); + } + { + Json_writer w; + w.start_array(); + w.add_member("zzz"); + w.start_array(); + ok(w.invalid_json, "Named array in an array"); + } + + { + Json_writer w; + w.start_array(); + w.end_object(); + ok(w.invalid_json, "JSON object end of array"); + } + + { + Json_writer w; + w.start_object(); + w.end_array(); + ok(w.invalid_json, "JSON array end of object"); + } + + + + diag("Done"); + + return exit_status(); +} + diff --git a/unittest/strings/CMakeLists.txt b/unittest/strings/CMakeLists.txt new file mode 100644 index 00000000..0896e132 --- /dev/null +++ b/unittest/strings/CMakeLists.txt @@ -0,0 +1,3 @@ + +MY_ADD_TESTS(strings json LINK_LIBRARIES strings mysys) + diff --git a/unittest/strings/json-t.c b/unittest/strings/json-t.c new file mode 100644 index 00000000..7c5f7957 --- /dev/null +++ b/unittest/strings/json-t.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2019, MariaDB Corporation + + 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 <tap.h> +#include <my_sys.h> +#include <json_lib.h> + +const char *json="{\"int\":1,\"str\":\"foo bar\"," + "\"array\":[10,20,{\"c\":\"d\"}],\"bool\":false}"; + +const char *json_ar="[1,\"foo bar\",[10,20,{\"c\":\"d\"}],false]"; + +const char *json_w="{\"int\" : 1 , " + "\"array\" : [10,20,{\"c\":\"d\"}] , \"bool\" : false }"; +const char *json_1="{ \"str\" : \"foo bar\" }"; + +void do_json(const char *key, int type, const char *value) +{ + enum json_types value_type; + const char *value_start; + int value_len; + + value_type= json_get_object_key(json, json + strlen(json), + key, &value_start, &value_len); + if (type) + ok(value_type == type && value_len == (int)strlen(value) && !memcmp(value_start, value, value_len), + "%s: type=%u, value(%d)=\"%.*s\"", key, value_type, value_len, value_len, value_start); + else + ok(value_type == type && value_len == (int)strlen(value), + "%s: type=%u keys=%u end=\"%s\"", key, value_type, value_len, value_start); +} + +void do_json_ar(int n, int type, const char *value) +{ + enum json_types value_type; + const char *value_start; + int value_len; + + value_type= json_get_array_item(json_ar, json_ar + strlen(json_ar), + n, &value_start, &value_len); + if (type) + ok(value_type == type && value_len == (int)strlen(value) && !memcmp(value_start, value, value_len), + "%i: type=%u, value(%d)=\"%.*s\"", n, value_type, value_len, value_len, value_start); + else + ok(value_type == type && value_len == (int)strlen(value), + "%i: type=%u keys=%u end=\"%s\"", n, value_type, value_len, value_start); +} + +void do_json_locate(const char *json, const char *key, int from, int to, int cp) +{ + const char *key_start, *key_end; + int res, comma_pos; + + res= json_locate_key(json, json + strlen(json), + key, &key_start, &key_end, &comma_pos); + if (key_start) + ok(res == 0 && key_start - json == from && key_end - json == to && + comma_pos == cp, "%s: [%d,%d,%d] %.*s%s", key, (int)(key_start-json), + (int)(key_end-json), comma_pos, (int)(key_start - json), json, key_end); + else + ok(res == 0 && from == -1, "%s: key not found", key); +} + +int main() +{ + plan(18); + + diag("%s", json); + do_json("int", 4, "1"); + do_json("str", 3, "foo bar"); + do_json("bool", 6, "false"); + do_json("c", 0, "1234"); + do_json("array", 2, "[10,20,{\"c\":\"d\"}]"); + diag("%s", json_ar); + do_json_ar(0, 4, "1"); + do_json_ar(1, 3, "foo bar"); + do_json_ar(2, 2, "[10,20,{\"c\":\"d\"}]"); + do_json_ar(3, 6, "false"); + do_json_ar(4, 0, "1234"); + + do_json_locate(json, "bool", 50, 63, 1); + do_json_locate(json, "int", 1, 9, 2); + do_json_locate(json, "array", 24, 50, 1); + do_json_locate(json_w, "bool", 43, 61, 1); + do_json_locate(json_w, "int", 1, 12, 2); + do_json_locate(json_w, "array", 11, 43, 1); + do_json_locate(json_w, "c", -1, -1, -1); + do_json_locate(json_1, "str", 1, 22, 0); + + return exit_status(); +} diff --git a/unittest/strings/strings-t.c b/unittest/strings/strings-t.c new file mode 100644 index 00000000..7532244b --- /dev/null +++ b/unittest/strings/strings-t.c @@ -0,0 +1,1545 @@ +/* Copyright (c) 2010, Oracle and/or its affiliates + Copyright (c) 2009, 2020, MariaDB Corporation. + + 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-1335 USA */ + +#include <tap.h> +#include <my_global.h> +#include <my_sys.h> + + +/* + U+00DF LATIN SMALL LETTER SHARP S = _utf8 x'C39F' = _latin1 x'DF' +*/ + +#define UTF8_sz "\xC3\x9F" +#define LATIN1_sz "\xDF" + +/* + U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE = _utf8 x'C385' +*/ + +#define UTF8_ARING "\xC3\x85" + +/* + U+00E4 LATIN SMALL LETTER A WITH DIAERESIS = _utf8 x'C3A4' +*/ +#define UTF8_auml "\xC3\xA4" +#define LATIN1_auml "\xE4" + +#define UCS2_a "\x00\x61" +#define UCS2_b "\x00\x62" +#define UCS2_sp "\x00\x20" + + +/* + Test that like_range() returns well-formed results. +*/ +static int +test_like_range_for_charset(CHARSET_INFO *cs, const char *src, size_t src_len) +{ + char min_str[32], max_str[32]; + size_t min_len, max_len, min_well_formed_len, max_well_formed_len; + int error= 0; + + my_ci_like_range(cs, src, src_len, '\\', '_', '%', + sizeof(min_str), min_str, max_str, &min_len, &max_len); + diag("min_len=%d\tmax_len=%d\t%s", (int) min_len, (int) max_len, + cs->coll_name.str); + min_well_formed_len= my_well_formed_length(cs, + min_str, min_str + min_len, + 10000, &error); + max_well_formed_len= my_well_formed_length(cs, + max_str, max_str + max_len, + 10000, &error); + if (min_len != min_well_formed_len) + diag("Bad min_str: min_well_formed_len=%d min_str[%d]=0x%02X", + (int) min_well_formed_len, (int) min_well_formed_len, + (uchar) min_str[min_well_formed_len]); + if (max_len != max_well_formed_len) + diag("Bad max_str: max_well_formed_len=%d max_str[%d]=0x%02X", + (int) max_well_formed_len, (int) max_well_formed_len, + (uchar) max_str[max_well_formed_len]); + return + min_len == min_well_formed_len && + max_len == max_well_formed_len ? 0 : 1; +} + + +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_utf8mb3 + &my_charset_utf8mb3_general_ci, +#ifdef HAVE_UCA_COLLATIONS + &my_charset_utf8mb3_unicode_ci, +#endif + &my_charset_utf8mb3_bin, +#endif +}; + + +typedef struct +{ + const char *a; + size_t alen; + const char *b; + size_t blen; + int res; +} STRNNCOLL_PARAM; + + +#define CSTR(x) (x),(sizeof(x)-1) + +/* + Byte sequence types used in the tests: + 8BIT - a 8 bit byte (>=00x80) which makes a single byte characters + MB2 - two bytes that make a valid character + H2 - a byte which is a valid MB2 head byte + T2 - a byte which is a valid MB2 tail byte + ILSEQ - a byte which makes an illegal sequence + H2+ILSEQ - a sequence that starts with a valid H2 byte, + but not followed by a valid T2 byte. + + Charset H2 T2 8BIT + ------- ---------------- --------------- -------- + big5 [A1..F9] [40..7E,A1..FE] + euckr [81..FE] [41..5A,61..7A,81..FE] + gb2312 [A1..F7] [A1..FE] + gbk [81..FE] [40..7E,80..FE] + + cp932 [81..9F,E0..FC] [40..7E,80..FC] [A1..DF] + sjis [81..9F,E0..FC] [40..7E,80..FC] [A1..DF] + + + Essential byte sequences in various character sets: + + Sequence big5 cp932 euckr gb2312 gbk sjis + -------- ---- ----- ----- ------ --- ---- + 80 ILSEQ ILSEQ ILSEQ ILSEQ ILSEQ ILSEQ + 81 ILSEQ H2 H2 ILSEQ H2 H2 + A1 H2 8BIT H2 H2 H2 8BIT + A1A1 MB2 8BIT+8BIT MB2 MB2 MB2 8BIT+8BIT + E0E0 MB2 MB2 MB2 MB2 MB2 MB2 + F9FE MB2 H2+ILSEQ MB2 ILSEQ+T2 MB2 H2+ILSEQ +*/ + + +/* + For character sets that have the following byte sequences: + 80 - ILSEQ + 81 - ILSEQ or H2 + F9 - ILSEQ or H2 + A1A1 - MB2 or 8BIT+8BIT + E0E0 - MB2 +*/ +static STRNNCOLL_PARAM strcoll_mb2_common[]= +{ + /* Compare two good sequences */ + {CSTR(""), CSTR(""), 0}, + {CSTR(""), CSTR(" "), 0}, + {CSTR(""), CSTR("A"), -1}, + {CSTR(""), CSTR("a"), -1}, + {CSTR(""), CSTR("\xA1\xA1"), -1}, + {CSTR(""), CSTR("\xE0\xE0"), -1}, + + {CSTR(" "), CSTR(""), 0}, + {CSTR(" "), CSTR(" "), 0}, + {CSTR(" "), CSTR("A"), -1}, + {CSTR(" "), CSTR("a"), -1}, + {CSTR(" "), CSTR("\xA1\xA1"), -1}, + {CSTR(" "), CSTR("\xE0\xE0"), -1}, + + {CSTR("a"), CSTR(""), 1}, + {CSTR("a"), CSTR(" "), 1}, + {CSTR("a"), CSTR("a"), 0}, + {CSTR("a"), CSTR("\xA1\xA1"), -1}, + {CSTR("a"), CSTR("\xE0\xE0"), -1}, + + {CSTR("\xA1\xA1"), CSTR("\xA1\xA1"), 0}, + {CSTR("\xA1\xA1"), CSTR("\xE0\xE0"), -1}, + + /* Compare a good character to an illegal or an incomplete sequence */ + {CSTR(""), CSTR("\x80"), -1}, + {CSTR(""), CSTR("\x81"), -1}, + {CSTR(""), CSTR("\xF9"), -1}, + + {CSTR(" "), CSTR("\x80"), -1}, + {CSTR(" "), CSTR("\x81"), -1}, + {CSTR(" "), CSTR("\xF9"), -1}, + + {CSTR("a"), CSTR("\x80"), -1}, + {CSTR("a"), CSTR("\x81"), -1}, + {CSTR("a"), CSTR("\xF9"), -1}, + + {CSTR("\xA1\xA1"), CSTR("\x80"), -1}, + {CSTR("\xA1\xA1"), CSTR("\x81"), -1}, + {CSTR("\xA1\xA1"), CSTR("\xF9"), -1}, + + {CSTR("\xE0\xE0"), CSTR("\x80"), -1}, + {CSTR("\xE0\xE0"), CSTR("\x81"), -1}, + {CSTR("\xE0\xE0"), CSTR("\xF9"), -1}, + + /* Compare two bad/incomplete sequences */ + {CSTR("\x80"), CSTR("\x80"), 0}, + {CSTR("\x80"), CSTR("\x81"), -1}, + {CSTR("\x80"), CSTR("\xF9"), -1}, + {CSTR("\x81"), CSTR("\x81"), 0}, + {CSTR("\x81"), CSTR("\xF9"), -1}, + + {NULL, 0, NULL, 0, 0} +}; + + +/* + For character sets that have good mb2 characters A1A1 and F9FE +*/ +static STRNNCOLL_PARAM strcoll_mb2_A1A1_mb2_F9FE[]= +{ + /* Compare two good characters */ + {CSTR(""), CSTR("\xF9\xFE"), -1}, + {CSTR(" "), CSTR("\xF9\xFE"), -1}, + {CSTR("a") , CSTR("\xF9\xFE"), -1}, + {CSTR("\xA1\xA1"), CSTR("\xF9\xFE"), -1}, + {CSTR("\xF9\xFE"), CSTR("\xF9\xFE"), 0}, + + /* Compare a good character to an illegal or an incomplete sequence */ + {CSTR(""), CSTR("\xA1"), -1}, + {CSTR(""), CSTR("\xF9"), -1}, + {CSTR("a"), CSTR("\xA1"), -1}, + {CSTR("a"), CSTR("\xF9"), -1}, + + {CSTR("\xA1\xA1"), CSTR("\xA1"), -1}, + {CSTR("\xA1\xA1"), CSTR("\xF9"), -1}, + + {CSTR("\xF9\xFE"), CSTR("\x80"), -1}, + {CSTR("\xF9\xFE"), CSTR("\x81"), -1}, + {CSTR("\xF9\xFE"), CSTR("\xA1"), -1}, + {CSTR("\xF9\xFE"), CSTR("\xF9"), -1}, + + /* Compare two bad/incomplete sequences */ + {CSTR("\x80"), CSTR("\xA1"), -1}, + {CSTR("\x80"), CSTR("\xF9"), -1}, + + {NULL, 0, NULL, 0, 0} +}; + + +/* + For character sets that have: + A1A1 - a good mb2 character + F9FE - a bad sequence +*/ +static STRNNCOLL_PARAM strcoll_mb2_A1A1_bad_F9FE[]= +{ + /* Compare a good character to an illegal or an incomplete sequence */ + {CSTR(""), CSTR("\xF9\xFE"), -1}, + {CSTR(" "), CSTR("\xF9\xFE"), -1}, + {CSTR("a") , CSTR("\xF9\xFE"), -1}, + {CSTR("\xA1\xA1"), CSTR("\xF9\xFE"), -1}, + + {CSTR(""), CSTR("\xA1"), -1}, + {CSTR(""), CSTR("\xF9"), -1}, + {CSTR("a"), CSTR("\xA1"), -1}, + {CSTR("a"), CSTR("\xF9"), -1}, + + {CSTR("\xA1\xA1"), CSTR("\xA1"), -1}, + {CSTR("\xA1\xA1"), CSTR("\xF9"), -1}, + + /* Compare two bad/incomplete sequences */ + {CSTR("\xF9\xFE"), CSTR("\x80"), 1}, + {CSTR("\xF9\xFE"), CSTR("\x81"), 1}, + {CSTR("\xF9\xFE"), CSTR("\xA1"), 1}, + {CSTR("\xF9\xFE"), CSTR("\xF9"), 1}, + {CSTR("\x80"), CSTR("\xA1"), -1}, + {CSTR("\x80"), CSTR("\xF9"), -1}, + {CSTR("\xF9\xFE"), CSTR("\xF9\xFE"), 0}, + + {NULL, 0, NULL, 0, 0} +}; + + +/* + For character sets that have: + 80 - ILSEQ or H2 + 81 - ILSEQ or H2 + A1 - 8BIT + F9 - ILSEQ or H2 + F9FE - a bad sequence (ILSEQ+XX or H2+ILSEQ) +*/ +static STRNNCOLL_PARAM strcoll_mb1_A1_bad_F9FE[]= +{ + /* Compare two good characters */ + {CSTR(""), CSTR("\xA1"), -1}, + {CSTR("\xA1\xA1"), CSTR("\xA1"), 1}, + + /* Compare a good character to an illegal or an incomplete sequence */ + {CSTR(""), CSTR("\xF9"), -1}, + {CSTR(""), CSTR("\xF9\xFE"), -1}, + {CSTR(" "), CSTR("\xF9\xFE"), -1}, + {CSTR("a"), CSTR("\xF9\xFE"), -1}, + {CSTR("a"), CSTR("\xA1"), -1}, + {CSTR("a"), CSTR("\xF9"), -1}, + + {CSTR("\xA1\xA1"), CSTR("\xF9"), -1}, + {CSTR("\xA1\xA1"), CSTR("\xF9\xFE"), -1}, + + {CSTR("\xF9\xFE"), CSTR("\x80"), 1}, + {CSTR("\xF9\xFE"), CSTR("\x81"), 1}, + {CSTR("\xF9\xFE"), CSTR("\xA1"), 1}, + {CSTR("\xF9\xFE"), CSTR("\xF9"), 1}, + + {CSTR("\x80"), CSTR("\xA1"), 1}, + + /* Compare two bad/incomplete sequences */ + {CSTR("\x80"), CSTR("\xF9"), -1}, + {CSTR("\xF9\xFE"), CSTR("\xF9\xFE"), 0}, + + {NULL, 0, NULL, 0, 0} +}; + + +/* + For character sets (e.g. cp932 and sjis) that have: + 8181 - a valid MB2 character + A1 - a valid 8BIT character + E0E0 - a valid MB2 character + and sort in this order: + 8181 < A1 < E0E0 +*/ +static STRNNCOLL_PARAM strcoll_8181_A1_E0E0[]= +{ + {CSTR("\x81\x81"), CSTR("\xA1"), -1}, + {CSTR("\x81\x81"), CSTR("\xE0\xE0"), -1}, + {CSTR("\xA1"), CSTR("\xE0\xE0"), -1}, + + {NULL, 0, NULL, 0, 0} +}; + + +/* + A shared test for eucjpms and ujis. +*/ +static STRNNCOLL_PARAM strcoll_ujis[]= +{ + {CSTR("\x8E\xA1"), CSTR("\x8E"), -1}, /* Good MB2 vs incomplete MB2 */ + {CSTR("\x8E\xA1"), CSTR("\x8F\xA1"), -1}, /* Good MB2 vs incomplete MB3 */ + {CSTR("\x8E\xA1"), CSTR("\x8F\xA1\xA1"), -1}, /* Good MB2 vs good MB3 */ + {CSTR("\xA1\xA1"), CSTR("\x8F\xA1\xA1"), 1}, /* Good MB2 vs good MB3 */ + {CSTR("\x8E"), CSTR("\x8F\xA1"), -1}, /* Incomplete MB2 vs incomplete MB3 */ + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf8mb3_common[]= +{ + {CSTR("\xC0"), CSTR("\xC1"), -1}, /* Unused byte vs unused byte */ + {CSTR("\xC0"), CSTR("\xFF"), -1}, /* Unused byte vs unused byte */ + {CSTR("\xC2\xA1"), CSTR("\xC0"), -1}, /* MB2 vs unused byte */ + {CSTR("\xC2\xA1"), CSTR("\xC2"), -1}, /* MB2 vs incomplete MB2 */ + {CSTR("\xC2\xA1"), CSTR("\xC2\xA2"), -1}, /* MB2 vs MB2 */ + {CSTR("\xC2\xA1"), CSTR("\xE0\xA0\x7F"),-1}, /* MB2 vs broken MB3 */ + {CSTR("\xC2\xA1"), CSTR("\xE0\xA0\x80"),-1}, /* MB2 vs MB3 */ + {CSTR("\xC2\xA1"), CSTR("\xE0\xA0\xBF"),-1}, /* MB2 vs MB3 */ + {CSTR("\xC2\xA1"), CSTR("\xE0\xA0\xC0"),-1}, /* MB2 vs broken MB3 */ + {CSTR("\xC2\xA1"), CSTR("\xE0\xA0"), -1}, /* MB2 vs incomplete MB3 */ + {CSTR("\xE0\xA0\x7E"), CSTR("\xE0\xA0\x7F"),-1},/* Broken MB3 vs broken MB3 */ + {CSTR("\xE0\xA0\x80"), CSTR("\xE0\xA0"), -1},/* MB3 vs incomplete MB3 */ + {CSTR("\xE0\xA0\x80"), CSTR("\xE0\xA0\x7F"),-1},/* MB3 vs broken MB3 */ + {CSTR("\xE0\xA0\x80"), CSTR("\xE0\xA0\xBF"),-1},/* MB3 vs MB3 */ + {CSTR("\xE0\xA0\x80"), CSTR("\xE0\xA0\xC0"),-1},/* MB3 vs broken MB3 */ + {CSTR("\xE0\xA0\xC0"), CSTR("\xE0\xA0\xC1"),-1},/* Broken MB3 vs broken MB3 */ + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf8mb4_common[]= +{ + /* Minimum four-byte character: U+10000 == _utf8 0xF0908080 */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xC0"), -1}, /* MB4 vs unused byte */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xC2"), -1}, /* MB4 vs incomplete MB2 */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xE0\xA0\x7F"),-1}, /* MB4 vs broken MB3 */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xE0\xA0\xC0"),-1}, /* MB4 vs broken MB3 */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xE0\xA0"), -1}, /* MB4 vs incomplete MB3 */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xF0\x90\x80"),-1}, /* MB4 vs incomplete MB4 */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xF0\x90\x80\x7F"),-1},/* MB4 vs broken MB4 */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xF0\x90\x80\xC0"),-1},/* MB4 vs broken MB4 */ + + /* Maximum four-byte character: U+10FFFF == _utf8 0xF48FBFBF */ + {CSTR("\xF4\x8F\xBF\xBF"), CSTR("\xC0"), -1}, /* MB4 vs unused byte */ + {CSTR("\xF4\x8F\xBF\xBF"), CSTR("\xC2"), -1}, /* MB4 vs incomplete MB2 */ + {CSTR("\xF4\x8F\xBF\xBF"), CSTR("\xE0\xA0\x7F"),-1}, /* MB4 vs broken MB3 */ + {CSTR("\xF4\x8F\xBF\xBF"), CSTR("\xE0\xA0\xC0"),-1}, /* MB4 vs broken MB3 */ + {CSTR("\xF4\x8F\xBF\xBF"), CSTR("\xE0\xA0"), -1}, /* MB4 vs incomplete MB3 */ + {CSTR("\xF4\x8F\xBF\xBF"), CSTR("\xF0\x90\x80"),-1}, /* MB4 vs incomplete MB4 */ + {CSTR("\xF4\x8F\xBF\xBF"), CSTR("\xF0\x90\x80\x7F"),-1},/* MB4 vs broken MB4 */ + {CSTR("\xF4\x8F\xBF\xBF"), CSTR("\xF0\x90\x80\xC0"),-1},/* MB4 vs broken MB4 */ + + /* Broken MB4 vs incomplete/broken MB3 */ + {CSTR("\xF0\x90\x80\x7F"), CSTR("\xE0\xA0"), 1}, /* Broken MB4 vs incomplete MB3 */ + {CSTR("\xF0\x90\x80\x7F"), CSTR("\xE0\xA0\x7F"),1}, /* Broken MB4 vs broken MB3 */ + {CSTR("\xF0\x90\x80\x7F"), CSTR("\xE0\xA0\xC0"),1}, /* Broken MB4 vs broken MB3 */ + + /* + Broken MB4 vs incomplete MB4: + The three leftmost bytes are compared binary, the fourth byte is compared + to auto-padded space. + */ + {CSTR("\xF0\x90\x80\x1F"), CSTR("\xF0\x90\x80"),-1}, /* Broken MB4 vs incomplete MB4 */ + {CSTR("\xF0\x90\x80\x7E"), CSTR("\xF0\x90\x80"),1}, /* Broken MB4 vs incomplete MB4 */ + + /* Broken MB4 vs broken MB4 */ + {CSTR("\xF0\x90\x80\x7E"), CSTR("\xF0\x90\x80\x7F"),-1},/* Broken MB4 vs broken MB4 */ + {CSTR("\xF0\x90\x80\x7E"), CSTR("\xF0\x90\x80\xC0"),-1},/* Broken MB4 vs broken MB4 */ + + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf8mb4_general_ci[]= +{ + /* All non-BMP characters are equal in utf8mb4_general_ci */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xF0\x90\x80\x81"),0},/* Non-BMB MB4 vs non-BMP MB4 */ + {CSTR("\xF0\x90\x80\x80"), CSTR("\xF4\x8F\xBF\xBF"),0},/* Non-BMB MB4 vs non-BMP MB4 */ + {CSTR("\x00"), CSTR("\xF0\x90\x80\x80"),-1},/* U+0000 vs non-BMP MB4 */ + {CSTR("\x00"), CSTR("\xF0\x90\x80\x81"),-1},/* U+0000 vs non-BMP MB4 */ + {CSTR("\x00"), CSTR("\xF4\x8F\xBF\xBF"),-1},/* U+0000 vs non-BMP MB4 */ + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_ucs2_common[]= +{ + {CSTR("\xC0"), CSTR("\xC1"), -1}, /* Incomlete MB2 vs incomplete MB2 */ + {CSTR("\xC0"), CSTR("\xFF"), -1}, /* Incomlete MB2 vs incomplete MB2 */ + {CSTR("\xC2\xA1"), CSTR("\xC0"), -1}, /* MB2 vs incomplete MB2 */ + {CSTR("\xC2\xA1"), CSTR("\xC2"), -1}, /* MB2 vs incomplete MB2 */ + {CSTR("\xC2\xA0"), CSTR("\xC2\xA1"), -1}, /* MB2 vs MB2 */ + {CSTR("\xC2\xA1"), CSTR("\xC2\xA2"), -1}, /* MB2 vs MB2 */ + + {CSTR("\xFF\xFF"), CSTR("\x00"),-1}, /* MB2 vs incomplete */ + {CSTR("\xFF\xFF\xFF\xFF"), CSTR("\x00"),-1}, /* MB2+MB2 vs incomplete */ + {CSTR("\xFF\xFF\xFF\xFF"), CSTR("\x00\x00\x00"), 1},/* MB2+MB2 vs MB2+incomplete */ + + {NULL, 0, NULL, 0, 0} +}; + + +/* Tests that involve comparison to SPACE (explicit, or padded) */ +static STRNNCOLL_PARAM strcoll_ucs2_space[]= +{ + {CSTR("\x00\x1F"), CSTR("\x00\x20"), -1}, /* MB2 vs MB2 */ + {CSTR("\x00\x20"), CSTR("\x00\x21"), -1}, /* MB2 vs MB2 */ + {CSTR("\x00\x1F"), CSTR(""), -1}, /* MB2 vs empty */ + {CSTR("\x00\x20"), CSTR(""), 0}, /* MB2 vs empty */ + {CSTR("\x00\x21"), CSTR(""), 1}, /* MB2 vs empty */ + + {NULL, 0, NULL, 0, 0} +}; + + +/* Tests that involve comparison to SPACE (explicit, or padded) */ +static STRNNCOLL_PARAM strcoll_utf16le_space[]= +{ + {CSTR("\x1F\x00"), CSTR("\x20\x00"), -1}, /* MB2 vs MB2 */ + {CSTR("\x20\x00"), CSTR("\x21\x00"), -1}, /* MB2 vs MB2 */ + {CSTR("\x1F\x00"), CSTR(""), -1}, /* MB2 vs empty */ + {CSTR("\x20\x00"), CSTR(""), 0}, /* MB2 vs empty */ + {CSTR("\x21\x00"), CSTR(""), 1}, /* MB2 vs empty */ + + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf16_common[]= +{ + /* Minimum four-byte character: U+10000 == _utf16 0xD800DC00 */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xC0"), -1},/* MB4 vs incomplete MB2 */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xC2"), -1},/* MB4 vs incomplete MB2 */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xD8\x00\xDB\x00"),-1},/* MB4 vs broken MB4 */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xD8\x00\xE0\x00"),-1},/* MB4 vs broken MB4 */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xDC\x00"), -1},/* MB4 vs broken MB2 */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xD8\x00\xDC"), -1},/* MB4 vs incomplete MB4 */ + + /* Maximum four-byte character: U+10FFFF == _utf8 0xF48FBFBF */ + {CSTR("\xDB\xFF\xDF\xFF"), CSTR("\xC0"), -1},/* MB4 vs incomplete MB2 */ + {CSTR("\xDB\xFF\xDF\xFF"), CSTR("\xC2"), -1},/* MB4 vs incomplete MB2 */ + {CSTR("\xDB\xFF\xDF\xFF"), CSTR("\xD8\x00\xDB\x00"),-1},/* MB4 vs broken MB4 */ + {CSTR("\xDB\xFF\xDF\xFF"), CSTR("\xD8\x00\xE0\x00"),-1},/* MB4 vs broken MB4 */ + {CSTR("\xDB\xFF\xDF\xFF"), CSTR("\xDC\x00"), -1},/* MB4 vs broken MB2 */ + {CSTR("\xDB\xFF\xDF\xFF"), CSTR("\xDC\xFF\xDF"), -1},/* MB4 vs incomplete MB4 */ + + /* Broken MB4 vs broken MB4 */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xD8\x00\xDB\x01"),-1},/* Broken MB4 vs broken MB4 */ + {CSTR("\xDB\xFF\xE0\xFE"), CSTR("\xDB\xFF\xE0\xFF"),-1},/* Broken MB4 vs broken MB4 */ + + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf16_general_ci[]= +{ + /* All non-BMP characters are compared as equal */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xD8\x00\xDC\x01"), 0},/* Non-BMP MB4 vs non-BMP MB4 */ + {CSTR("\xD8\x00\xDC\x00"), CSTR("\xDB\xFF\xDF\xFF"), 0},/* Non-BMP MB4 vs non-BMP MB4 */ + {CSTR("\x00\x00"), CSTR("\xD8\x00\xDC\x01"),-1},/* U+0000 vs non-BMP MB4 */ + {CSTR("\x00\x00"), CSTR("\xDB\xFF\xDF\xFF"),-1},/* U+0000 vs non-BMP MB4 */ + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf16le_common[]= +{ + /* Minimum four-byte character: U+10000 == _utf16 0xD800DC00 */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\xC0"), -1},/* MB4 vs incomplete MB2 */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\xC2"), -1},/* MB4 vs incomplete MB2 */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\x00\xD8\x00\xDB"),-1},/* MB4 vs broken MB4 */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\x00\xD8\x00\xD0"),-1},/* MB4 vs broken MB4 */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\x00\xDC"), -1},/* MB4 vs broken MB2 */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\x00\xD8\x00"), -1},/* MB4 vs incomplete MB4 */ + + /* Maximum four-byte character: U+10FFFF == _utf8 0xF48FBFBF */ + {CSTR("\xFF\xDB\xFF\xDF"), CSTR("\xC0"), -1},/* MB4 vs incomplete MB2 */ + {CSTR("\xFF\xDB\xFF\xDF"), CSTR("\xC2"), -1},/* MB4 vs incomplete MB2 */ + {CSTR("\xFF\xDB\xFF\xDF"), CSTR("\x00\xD8\x00\xDB"),-1},/* MB4 vs broken MB4 */ + {CSTR("\xFF\xDB\xFF\xDF"), CSTR("\x00\xD8\x00\xE0"),-1},/* MB4 vs broken MB4 */ + {CSTR("\xFF\xDB\xFF\xDF"), CSTR("\x00\xDC"), -1},/* MB4 vs broken MB2 */ + {CSTR("\xFF\xDB\xFF\xDF"), CSTR("\xFF\xDC\x00"), -1},/* MB4 vs incomplete MB4 */ + + /* Broken MB4 vs broken MB4 */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\x00\xD8\x01\xDB"),-1},/* Broken MB4 vs broken MB4 */ + {CSTR("\xFF\xDB\xFE\xE0"), CSTR("\xFF\xDB\xFF\xE0"),-1},/* Broken MB4 vs broken MB4 */ + + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf16le_general_ci[]= +{ + /* All non-BMP characters are compared as equal */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\x00\xD8\x01\xDC"), 0},/* Non-BMP MB4 vs non-BMP MB4 */ + {CSTR("\x00\xD8\x00\xDC"), CSTR("\xFF\xDB\xFF\xDF"), 0},/* Non-BMP MB4 vs non-BMP MB4 */ + {CSTR("\x00\x00"), CSTR("\x00\xD8\x01\xDC"), -1},/* U+0000 vs non-BMP MB4 */ + {CSTR("\x00\x00"), CSTR("\xFF\xDB\xFF\xDF"), -1},/* U+0000 vs non-BMP MB4 */ + + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf32_common[]= +{ + /* Minimum character: U+0000 == _utf32 0x00000000 */ + {CSTR("\x00\x00\x00\x00"), CSTR("\x00"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x00\x00\x00"), CSTR("\xFF"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x00\x00\x00"), CSTR("\x00\x00"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x00\x00\x00"), CSTR("\x00\x00\x00"),-1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x00\x00\x00"), CSTR("\x00\x20\x00\x00"),-1},/* MB4 vs broken MB4 */ + {CSTR("\x00\x00\x00\x00"), CSTR("\xFF\xFF\xFF\xFF"),-1},/* MB4 vs broken MB4 */ + + /* Minimum non-BMP character: U+10000 == _utf32 0x00010000 */ + {CSTR("\x00\x01\x00\x00"), CSTR("\x00"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x01\x00\x00"), CSTR("\xFF"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x01\x00\x00"), CSTR("\x00\x00"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x01\x00\x00"), CSTR("\x00\x00\x00"),-1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x01\x00\x00"), CSTR("\x00\x20\x00\x00"),-1},/* MB4 vs broken MB4 */ + {CSTR("\x00\x01\x00\x00"), CSTR("\xFF\xFF\xFF\xFF"),-1},/* MB4 vs broken MB4 */ + + /* Maximum character: U+10FFFF == _utf32 0x0010FFFF */ + {CSTR("\x00\x10\xFF\xFF"), CSTR("\x00"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x10\xFF\xFF"), CSTR("\xFF"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x10\xFF\xFF"), CSTR("\x00\x00"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x10\xFF\xFF"), CSTR("\x00\x00\x00"), -1}, /* MB4 vs incomplete MB4 */ + {CSTR("\x00\x10\xFF\xFF"), CSTR("\x20\x00\x00\x00"),-1},/* MB4 vs broken MB3 */ + {CSTR("\x00\x10\xFF\xFF"), CSTR("\xFF\xFF\xFF\xFF"),-1},/* MB4 vs broken MB4 */ + + + /* Broken MB4 vs incomplete/broken MB3 */ + {CSTR("\x00\x20\x00\x00"), CSTR("\x00"), 1}, /* Broken MB4 vs incomplete MB4 */ + {CSTR("\x00\x20\x00\x00"), CSTR("\x00\x00"), 1}, /* Broken MB4 vs incomplete MB4 */ + {CSTR("\x00\x20\x00\x00"), CSTR("\x00\x00\x00"), 1}, /* Broken MB4 vs incomplete MB4 */ + {CSTR("\x00\x20\x00\x00"), CSTR("\x00\x20\x00\x01"),-1},/* Broken MB4 vs broken MB4 */ + + {NULL, 0, NULL, 0, 0} +}; + + +static STRNNCOLL_PARAM strcoll_utf32_general_ci[]= +{ + /* Two non-BMP characters are compared as equal */ + {CSTR("\x00\x01\x00\x00"), CSTR("\x00\x01\x00\x01"), 0}, /* non-BMP MB4 vs non-BMP MB4 */ + {CSTR("\x00\x00\x00\x00"), CSTR("\x00\x01\x00\x00"), -1}, /* U+0000 vs non-BMP MB4 */ + {CSTR("\x00\x00\x00\x00"), CSTR("\x00\x01\x00\x01"), -1}, /* U+0000 vs non-BMP MB4 */ + + {NULL, 0, NULL, 0, 0} +}; + + +static void +str2hex(char *dst, size_t dstlen, const char *src, size_t srclen) +{ + char *dstend= dst + dstlen; + const char *srcend= src + srclen; + for (*dst= '\0' ; dst + 3 < dstend && src < srcend; ) + { + sprintf(dst, "%02X", (unsigned char) src[0]); + dst+=2; + src++; + } +} + + +/* + Check if the two comparison result are semantically equal: + both are negative, both are positive, or both are zero. +*/ +static int +eqres(int ares, int bres) +{ + return (ares < 0 && bres < 0) || + (ares > 0 && bres > 0) || + (ares == 0 && bres == 0); +} + + +static int +strcollsp(CHARSET_INFO *cs, const STRNNCOLL_PARAM *param) +{ + int failed= 0; + const STRNNCOLL_PARAM *p; + diag("%-20s %-10s %-10s %10s %10s", "Collation", "a", "b", "ExpectSign", "Actual"); + for (p= param; p->a; p++) + { + char ahex[64], bhex[64]; + int res= my_ci_strnncollsp(cs, (const uchar *) p->a, p->alen, + (const uchar *) p->b, p->blen); + str2hex(ahex, sizeof(ahex), p->a, p->alen); + str2hex(bhex, sizeof(bhex), p->b, p->blen); + diag("%-20s %-10s %-10s %10d %10d%s", + cs->coll_name.str, ahex, bhex, p->res, res, + eqres(res, p->res) ? "" : " FAILED"); + if (!eqres(res, p->res)) + { + failed++; + } + else + { + /* Test in reverse order */ + res= my_ci_strnncollsp(cs, (const uchar *) p->b, p->blen, + (const uchar *) p->a, p->alen); + if (!eqres(res, -p->res)) + { + diag("Comparison in reverse order failed. Expected %d, got %d", + -p->res, res); + failed++; + } + } + } + return failed; +} + + +static int +test_strcollsp() +{ + int failed= 0; +#ifdef HAVE_CHARSET_big5 + failed+= strcollsp(&my_charset_big5_chinese_ci, strcoll_mb2_common); + failed+= strcollsp(&my_charset_big5_chinese_ci, strcoll_mb2_A1A1_mb2_F9FE); + failed+= strcollsp(&my_charset_big5_bin, strcoll_mb2_common); + failed+= strcollsp(&my_charset_big5_bin, strcoll_mb2_A1A1_mb2_F9FE); +#endif +#ifdef HAVE_CHARSET_cp932 + failed+= strcollsp(&my_charset_cp932_japanese_ci, strcoll_mb2_common); + failed+= strcollsp(&my_charset_cp932_japanese_ci, strcoll_mb1_A1_bad_F9FE); + failed+= strcollsp(&my_charset_cp932_bin, strcoll_mb2_common); + failed+= strcollsp(&my_charset_cp932_bin, strcoll_mb1_A1_bad_F9FE); + failed+= strcollsp(&my_charset_cp932_japanese_ci, strcoll_8181_A1_E0E0); + failed+= strcollsp(&my_charset_cp932_bin, strcoll_8181_A1_E0E0); +#endif +#ifdef HAVE_CHARSET_eucjpms + failed+= strcollsp(&my_charset_eucjpms_japanese_ci, strcoll_mb2_common); + failed+= strcollsp(&my_charset_eucjpms_bin, strcoll_mb2_common); + failed+= strcollsp(&my_charset_eucjpms_japanese_ci, strcoll_mb2_A1A1_mb2_F9FE); + failed+= strcollsp(&my_charset_eucjpms_bin, strcoll_mb2_A1A1_mb2_F9FE); + failed+= strcollsp(&my_charset_eucjpms_japanese_ci, strcoll_ujis); + failed+= strcollsp(&my_charset_eucjpms_bin, strcoll_ujis); +#endif +#ifdef HAVE_CHARSET_euckr + failed+= strcollsp(&my_charset_euckr_korean_ci, strcoll_mb2_common); + failed+= strcollsp(&my_charset_euckr_korean_ci, strcoll_mb2_A1A1_mb2_F9FE); + failed+= strcollsp(&my_charset_euckr_bin, strcoll_mb2_common); + failed+= strcollsp(&my_charset_euckr_bin, strcoll_mb2_A1A1_mb2_F9FE); +#endif +#ifdef HAVE_CHARSET_gb2312 + failed+= strcollsp(&my_charset_gb2312_chinese_ci, strcoll_mb2_common); + failed+= strcollsp(&my_charset_gb2312_chinese_ci, strcoll_mb2_A1A1_bad_F9FE); + failed+= strcollsp(&my_charset_gb2312_bin, strcoll_mb2_common); + failed+= strcollsp(&my_charset_gb2312_bin, strcoll_mb2_A1A1_bad_F9FE); +#endif +#ifdef HAVE_CHARSET_gbk + failed+= strcollsp(&my_charset_gbk_chinese_ci, strcoll_mb2_common); + failed+= strcollsp(&my_charset_gbk_chinese_ci, strcoll_mb2_A1A1_mb2_F9FE); + failed+= strcollsp(&my_charset_gbk_bin, strcoll_mb2_common); + failed+= strcollsp(&my_charset_gbk_bin, strcoll_mb2_A1A1_mb2_F9FE); +#endif +#ifdef HAVE_CHARSET_sjis + failed+= strcollsp(&my_charset_sjis_japanese_ci, strcoll_mb2_common); + failed+= strcollsp(&my_charset_sjis_bin, strcoll_mb2_common); + failed+= strcollsp(&my_charset_sjis_japanese_ci, strcoll_mb1_A1_bad_F9FE); + failed+= strcollsp(&my_charset_sjis_bin, strcoll_mb1_A1_bad_F9FE); + failed+= strcollsp(&my_charset_sjis_japanese_ci, strcoll_8181_A1_E0E0); + failed+= strcollsp(&my_charset_sjis_bin, strcoll_8181_A1_E0E0); +#endif +#ifdef HAVE_CHARSET_ucs2 + failed+= strcollsp(&my_charset_ucs2_general_ci, strcoll_ucs2_common); + failed+= strcollsp(&my_charset_ucs2_general_ci, strcoll_ucs2_space); + failed+= strcollsp(&my_charset_ucs2_bin, strcoll_ucs2_common); + failed+= strcollsp(&my_charset_ucs2_bin, strcoll_ucs2_space); +#endif +#ifdef HAVE_CHARSET_ujis + failed+= strcollsp(&my_charset_ujis_japanese_ci, strcoll_mb2_common); + failed+= strcollsp(&my_charset_ujis_bin, strcoll_mb2_common); + failed+= strcollsp(&my_charset_ujis_japanese_ci, strcoll_mb2_A1A1_mb2_F9FE); + failed+= strcollsp(&my_charset_ujis_bin, strcoll_mb2_A1A1_mb2_F9FE); + failed+= strcollsp(&my_charset_ujis_japanese_ci, strcoll_ujis); + failed+= strcollsp(&my_charset_ujis_bin, strcoll_ujis); +#endif +#ifdef HAVE_CHARSET_utf16 + failed+= strcollsp(&my_charset_utf16_general_ci, strcoll_ucs2_common); + failed+= strcollsp(&my_charset_utf16_general_ci, strcoll_ucs2_space); + failed+= strcollsp(&my_charset_utf16_general_ci, strcoll_utf16_common); + failed+= strcollsp(&my_charset_utf16_general_ci, strcoll_utf16_general_ci); + failed+= strcollsp(&my_charset_utf16_bin, strcoll_ucs2_common); + failed+= strcollsp(&my_charset_utf16_bin, strcoll_ucs2_space); + failed+= strcollsp(&my_charset_utf16_bin, strcoll_utf16_common); + + failed+= strcollsp(&my_charset_utf16le_general_ci,strcoll_ucs2_common); + failed+= strcollsp(&my_charset_utf16le_general_ci,strcoll_utf16le_space); + failed+= strcollsp(&my_charset_utf16le_general_ci,strcoll_utf16le_common); + failed+= strcollsp(&my_charset_utf16le_general_ci,strcoll_utf16le_general_ci); + failed+= strcollsp(&my_charset_utf16le_bin, strcoll_ucs2_common); + failed+= strcollsp(&my_charset_utf16le_bin, strcoll_utf16le_space); + failed+= strcollsp(&my_charset_utf16le_bin, strcoll_utf16le_common); +#endif +#ifdef HAVE_CHARSET_utf32 + failed+= strcollsp(&my_charset_utf32_general_ci, strcoll_utf32_common); + failed+= strcollsp(&my_charset_utf32_general_ci, strcoll_utf32_general_ci); + failed+= strcollsp(&my_charset_utf32_bin, strcoll_utf32_common); +#endif +#ifdef HAVE_CHARSET_utf8 + failed+= strcollsp(&my_charset_utf8mb3_general_ci, strcoll_utf8mb3_common); + failed+= strcollsp(&my_charset_utf8mb3_general_mysql500_ci, strcoll_utf8mb3_common); + failed+= strcollsp(&my_charset_utf8mb3_bin, strcoll_utf8mb3_common); +#endif +#ifdef HAVE_CHARSET_utf8mb4 + failed+= strcollsp(&my_charset_utf8mb4_general_ci, strcoll_utf8mb3_common); + failed+= strcollsp(&my_charset_utf8mb4_bin, strcoll_utf8mb3_common); + failed+= strcollsp(&my_charset_utf8mb4_general_ci, strcoll_utf8mb4_common); + failed+= strcollsp(&my_charset_utf8mb4_general_ci, strcoll_utf8mb4_general_ci); + failed+= strcollsp(&my_charset_utf8mb4_bin, strcoll_utf8mb4_common); +#endif + return failed; +} + + +typedef struct +{ + size_t size; + size_t nchars; + LEX_CSTRING min; + LEX_CSTRING max; +} MINMAX_PARAM; + + +static MINMAX_PARAM minmax_param_latin1_swedish_ci[]= +{ + {0, 0, {CSTR("")}, {CSTR("")}}, + {0, 1, {CSTR("")}, {CSTR("")}}, + {0, 2, {CSTR("")}, {CSTR("")}}, + {0, 3, {CSTR("")}, {CSTR("")}}, + {1, 0, {CSTR("")}, {CSTR("")}}, + {1, 1, {CSTR("\x00")}, {CSTR("\xFF")}}, + {1, 2, {CSTR("\x00")}, {CSTR("\xFF")}}, + {1, 3, {CSTR("\x00")}, {CSTR("\xFF")}}, + {2, 0, {CSTR("")}, {CSTR("")}}, + {2, 1, {CSTR("\x00")}, {CSTR("\xFF")}}, + {2, 2, {CSTR("\x00\x00")}, {CSTR("\xFF\xFF")}}, + {2, 3, {CSTR("\x00\x00")}, {CSTR("\xFF\xFF")}}, + {3, 0, {CSTR("")}, {CSTR("")}}, + {3, 1, {CSTR("\x00")}, {CSTR("\xFF")}}, + {3, 2, {CSTR("\x00\x00")}, {CSTR("\xFF\xFF")}}, + {3, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xFF\xFF\xFF")}} +}; + + +static MINMAX_PARAM minmax_param_latin1_nopad_bin[]= +{ + {0, 0, {CSTR("")}, {CSTR("")}}, + {0, 1, {CSTR("")}, {CSTR("")}}, + {0, 2, {CSTR("")}, {CSTR("")}}, + {0, 3, {CSTR("")}, {CSTR("")}}, + {1, 0, {CSTR("")}, {CSTR("")}}, + {1, 1, {CSTR("")}, {CSTR("\xFF")}}, + {1, 2, {CSTR("")}, {CSTR("\xFF")}}, + {1, 3, {CSTR("")}, {CSTR("\xFF")}}, + {2, 0, {CSTR("")}, {CSTR("")}}, + {2, 1, {CSTR("")}, {CSTR("\xFF")}}, + {2, 2, {CSTR("")}, {CSTR("\xFF\xFF")}}, + {2, 3, {CSTR("")}, {CSTR("\xFF\xFF")}}, + {3, 0, {CSTR("")}, {CSTR("")}}, + {3, 1, {CSTR("")}, {CSTR("\xFF")}}, + {3, 2, {CSTR("")}, {CSTR("\xFF\xFF")}}, + {3, 3, {CSTR("")}, {CSTR("\xFF\xFF\xFF")}} +}; + + +static MINMAX_PARAM minmax_param_utf8mb3_unicode_ci[]= +{ + {0, 0, {CSTR("")}, {CSTR("")}}, + {0, 1, {CSTR("")}, {CSTR("")}}, + {0, 2, {CSTR("")}, {CSTR("")}}, + {0, 3, {CSTR("")}, {CSTR("")}}, + {1, 0, {CSTR("")}, {CSTR("")}}, + {1, 1, {CSTR("\x09")}, {CSTR("")}}, + {1, 2, {CSTR("\x09")}, {CSTR("")}}, + {1, 3, {CSTR("\x09")}, {CSTR("")}}, + {2, 0, {CSTR("")}, {CSTR("")}}, + {2, 1, {CSTR("\x09")}, {CSTR("")}}, + {2, 2, {CSTR("\x09\x09")}, {CSTR("")}}, + {2, 3, {CSTR("\x09\x09")}, {CSTR("")}}, + {3, 0, {CSTR("")}, {CSTR("")}}, + {3, 1, {CSTR("\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {3, 2, {CSTR("\x09\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {3, 3, {CSTR("\x09\x09\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {4, 0, {CSTR("")}, {CSTR("")}}, + {4, 1, {CSTR("\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {4, 2, {CSTR("\x09\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {4, 3, {CSTR("\x09\x09\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {5, 0, {CSTR("")}, {CSTR("")}}, + {5, 1, {CSTR("\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {5, 2, {CSTR("\x09\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {5, 3, {CSTR("\x09\x09\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {6, 0, {CSTR("")}, {CSTR("")}}, + {6, 1, {CSTR("\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {6, 2, {CSTR("\x09\x09")}, {CSTR("\xEF\xBF\xBF\xEF\xBF\xBF")}}, + {6, 3, {CSTR("\x09\x09\x09")}, {CSTR("\xEF\xBF\xBF\xEF\xBF\xBF")}}, + {7, 0, {CSTR("")}, {CSTR("")}}, + {7, 1, {CSTR("\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {7, 2, {CSTR("\x09\x09")}, {CSTR("\xEF\xBF\xBF\xEF\xBF\xBF")}}, + {7, 3, {CSTR("\x09\x09\x09")}, {CSTR("\xEF\xBF\xBF\xEF\xBF\xBF")}}, + {8, 0, {CSTR("")}, {CSTR("")}}, + {8, 1, {CSTR("\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {8, 2, {CSTR("\x09\x09")}, {CSTR("\xEF\xBF\xBF\xEF\xBF\xBF")}}, + {8, 3, {CSTR("\x09\x09\x09")}, {CSTR("\xEF\xBF\xBF\xEF\xBF\xBF")}}, + {9, 0, {CSTR("")}, {CSTR("")}}, + {9, 1, {CSTR("\x09")}, {CSTR("\xEF\xBF\xBF")}}, + {9, 2, {CSTR("\x09\x09")}, {CSTR("\xEF\xBF\xBF\xEF\xBF\xBF")}}, + {9, 3, {CSTR("\x09\x09\x09")}, {CSTR("\xEF\xBF\xBF\xEF\xBF\xBF\xEF\xBF\xBF")}}, +}; + + +#ifdef HAVE_CHARSET_big5 +static MINMAX_PARAM minmax_param_big5_chinese_ci[]= +{ + {0, 0, {CSTR("")}, {CSTR("")}}, + {0, 1, {CSTR("")}, {CSTR("")}}, + {0, 2, {CSTR("")}, {CSTR("")}}, + {0, 3, {CSTR("")}, {CSTR("")}}, + {1, 0, {CSTR("")}, {CSTR("")}}, + {1, 1, {CSTR("\x00")}, {CSTR("")}}, + {1, 2, {CSTR("\x00")}, {CSTR("")}}, + {1, 3, {CSTR("\x00")}, {CSTR("")}}, + {2, 0, {CSTR("")}, {CSTR("")}}, + {2, 1, {CSTR("\x00")}, {CSTR("\xF9\xD5")}}, + {2, 2, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5")}}, + {2, 3, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5")}}, + {3, 0, {CSTR("")}, {CSTR("")}}, + {3, 1, {CSTR("\x00")}, {CSTR("\xF9\xD5")}}, + {3, 2, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5")}}, + {3, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xF9\xD5")}}, + {4, 0, {CSTR("")}, {CSTR("")}}, + {4, 1, {CSTR("\x00")}, {CSTR("\xF9\xD5")}}, + {4, 2, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5")}}, + {4, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5")}}, + {5, 0, {CSTR("")}, {CSTR("")}}, + {5, 1, {CSTR("\x00")}, {CSTR("\xF9\xD5")}}, + {5, 2, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5")}}, + {5, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5")}}, + {6, 0, {CSTR("")}, {CSTR("")}}, + {6, 1, {CSTR("\x00")}, {CSTR("\xF9\xD5")}}, + {6, 2, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5")}}, + {6, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5\xF9\xD5")}}, + {7, 0, {CSTR("")}, {CSTR("")}}, + {7, 1, {CSTR("\x00")}, {CSTR("\xF9\xD5")}}, + {7, 2, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5")}}, + {7, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5\xF9\xD5")}}, + {8, 0, {CSTR("")}, {CSTR("")}}, + {8, 1, {CSTR("\x00")}, {CSTR("\xF9\xD5")}}, + {8, 2, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5")}}, + {8, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5\xF9\xD5")}}, + {9, 0, {CSTR("")}, {CSTR("")}}, + {9, 1, {CSTR("\x00")}, {CSTR("\xF9\xD5")}}, + {9, 2, {CSTR("\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5")}}, + {9, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xF9\xD5\xF9\xD5\xF9\xD5")}}, +}; +#endif + +#ifdef HAVE_CHARSET_cp1250 +static MINMAX_PARAM minmax_param_cp1250_czech_cs[]= +{ + {0, 0, {CSTR("")}, {CSTR("")}}, + {0, 1, {CSTR("")}, {CSTR("")}}, + {0, 2, {CSTR("")}, {CSTR("")}}, + {0, 3, {CSTR("")}, {CSTR("")}}, + {1, 0, {CSTR("")}, {CSTR("")}}, + {1, 1, {CSTR("\x00")}, {CSTR("\xFF")}}, + {1, 2, {CSTR("\x00")}, {CSTR("\xFF")}}, + {1, 3, {CSTR("\x00")}, {CSTR("\xFF")}}, + {2, 0, {CSTR("")}, {CSTR("")}}, + {2, 1, {CSTR("\x00")}, {CSTR("\xFF")}}, + {2, 2, {CSTR("\x00\x00")}, {CSTR("\xFF\xFF")}}, + {2, 3, {CSTR("\x00\x00")}, {CSTR("\xFF\xFF")}}, + {3, 0, {CSTR("")}, {CSTR("")}}, + {3, 1, {CSTR("\x00")}, {CSTR("\xFF")}}, + {3, 2, {CSTR("\x00\x00")}, {CSTR("\xFF\xFF")}}, + {3, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xFF\xFF\xFF")}} +}; +#endif + + +#ifdef HAVE_CHARSET_latin2 +static MINMAX_PARAM minmax_param_latin2_czech_cs[]= +{ + {0, 0, {CSTR("")}, {CSTR("")}}, + {0, 1, {CSTR("")}, {CSTR("")}}, + {0, 2, {CSTR("")}, {CSTR("")}}, + {0, 3, {CSTR("")}, {CSTR("")}}, + {1, 0, {CSTR("")}, {CSTR("")}}, + {1, 1, {CSTR("\x00")}, {CSTR("\xAE")}}, + {1, 2, {CSTR("\x00")}, {CSTR("\xAE")}}, + {1, 3, {CSTR("\x00")}, {CSTR("\xAE")}}, + {2, 0, {CSTR("")}, {CSTR("")}}, + {2, 1, {CSTR("\x00")}, {CSTR("\xAE")}}, + {2, 2, {CSTR("\x00\x00")}, {CSTR("\xAE\xAE")}}, + {2, 3, {CSTR("\x00\x00")}, {CSTR("\xAE\xAE")}}, + {3, 0, {CSTR("")}, {CSTR("")}}, + {3, 1, {CSTR("\x00")}, {CSTR("\xAE")}}, + {3, 2, {CSTR("\x00\x00")}, {CSTR("\xAE\xAE")}}, + {3, 3, {CSTR("\x00\x00\x00")}, {CSTR("\xAE\xAE\xAE")}} +}; +#endif + + +static int test_minmax_str_one(CHARSET_INFO *cs, + const MINMAX_PARAM *params, size_t count) +{ + size_t i; + int failed_total= 0; + for (i= 0; i < count; i++) + { + int failed; + char min[32], hmin[64]; + char max[32], hmax[64]; + const MINMAX_PARAM *prm= ¶ms[i]; + size_t minlen= cs->coll->min_str(cs, (uchar *) min, prm->size, + prm->nchars); + size_t maxlen= cs->coll->max_str(cs, (uchar *) max, prm->size, + prm->nchars); + failed= minlen != prm->min.length || memcmp(min, prm->min.str, minlen) || + maxlen != prm->max.length || memcmp(max, prm->max.str, maxlen); + + str2hex(hmin, sizeof(hmin), min, minlen); + str2hex(hmax, sizeof(hmax), max, maxlen); + diag("%-32s %2d %2d %-10s %-10s%s", + cs->coll_name.str, (int) prm->size, (int) prm->nchars, hmin, hmax, + failed ? " FAILED" : ""); + if (failed) + { + str2hex(hmin, sizeof(hmin), prm->min.str, prm->min.length); + str2hex(hmax, sizeof(hmax), prm->max.str, prm->max.length); + diag("%-40s %-10s %-10s EXPECTED", cs->coll_name.str, hmin, hmax); + } + failed_total+= failed; + } + return failed_total; +} + + +static int test_minmax_str() +{ + int failed= 0; + failed+= test_minmax_str_one(&my_charset_latin1_nopad_bin, + minmax_param_latin1_nopad_bin, + array_elements(minmax_param_latin1_nopad_bin)); + failed+= test_minmax_str_one(&my_charset_latin1, + minmax_param_latin1_swedish_ci, + array_elements(minmax_param_latin1_swedish_ci)); + failed+= test_minmax_str_one(&my_charset_utf8mb3_unicode_ci, + minmax_param_utf8mb3_unicode_ci, + array_elements(minmax_param_utf8mb3_unicode_ci)); +#ifdef HAVE_CHARSET_big5 + failed+= test_minmax_str_one(&my_charset_big5_chinese_ci, + minmax_param_big5_chinese_ci, + array_elements(minmax_param_big5_chinese_ci)); +#endif +#ifdef HAVE_CHARSET_cp1250 + failed+= test_minmax_str_one(&my_charset_cp1250_czech_cs, + minmax_param_cp1250_czech_cs, + array_elements(minmax_param_cp1250_czech_cs)); +#endif +#ifdef HAVE_CHARSET_latin2 + failed+= test_minmax_str_one(&my_charset_latin2_czech_cs, + minmax_param_latin2_czech_cs, + array_elements(minmax_param_latin2_czech_cs)); +#endif + return failed; +} + +typedef struct +{ + LEX_CSTRING a; + LEX_CSTRING b; + size_t nchars; + int res; +} STRNNCOLLSP_CHAR_PARAM; + + +/* + Some lines in the below test data are marked as follows: + + IF - An ignorable failure. The scanner finds an ignorable character + followed by a normal character (or by a contraction), + but the "nchars" limit allows only one character to be scanned. + The whole sequence is ignored an is treated as end-of-line. + CF - A contraction failure. The scanner finds a contraction consisting + of two characters, but the "nchars" limit allows only one character + to be scanned. The whole contraction is ignored and is treated + as end-of-line. +*/ + + +/* + Tests for mbminlen1 character sets, + for both PAD SPACE and NOPAD collations +*/ +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_mbminlen1_xpad_common[]= +{ + {{CSTR("a")}, {CSTR("a")}, 0, 0}, + {{CSTR("a")}, {CSTR("a")}, 1, 0}, + {{CSTR("a")}, {CSTR("a")}, 2, 0}, + {{CSTR("a")}, {CSTR("a")}, 3, 0}, + {{CSTR("a")}, {CSTR("a")}, 100, 0}, + + {{CSTR("a")}, {CSTR("ab")}, 0, 0}, + {{CSTR("a")}, {CSTR("ab")}, 1, 0}, + {{CSTR("a")}, {CSTR("ab")}, 2, -1}, + {{CSTR("a")}, {CSTR("ab")}, 3, -1}, + {{CSTR("a")}, {CSTR("ab")}, 100, -1}, + + {{CSTR("a")}, {CSTR("a ")}, 0, 0}, + {{CSTR("a")}, {CSTR("a ")}, 1, 0}, + {{CSTR("a")}, {CSTR("a ")}, 2, 0}, + {{CSTR("a")}, {CSTR("a ")}, 3, 0}, + {{CSTR("a")}, {CSTR("a ")}, 100, 0}, + + {{CSTR("a")}, {CSTR("a ")}, 0, 0}, + {{CSTR("a")}, {CSTR("a ")}, 1, 0}, + {{CSTR("a")}, {CSTR("a ")}, 2, 0}, + {{CSTR("a")}, {CSTR("a ")}, 3, 0}, + {{CSTR("a")}, {CSTR("a ")}, 100, 0}, + + {{CSTR("ss")}, {CSTR("ss")}, 0, 0}, + {{CSTR("ss")}, {CSTR("ss")}, 1, 0}, + {{CSTR("ss")}, {CSTR("ss")}, 2, 0}, + {{CSTR("ss")}, {CSTR("ss")}, 3, 0}, + {{CSTR("ss")}, {CSTR("ss")}, 100, 0}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +/* Tests for utf8, for both PAD SPACE and NOPAD collations */ +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_utf8mbx_xpad_common[]= +{ + {{CSTR(UTF8_sz)}, {CSTR(UTF8_sz)}, 0, 0}, + {{CSTR(UTF8_sz)}, {CSTR(UTF8_sz)}, 1, 0}, + {{CSTR(UTF8_sz)}, {CSTR(UTF8_sz)}, 2, 0}, + {{CSTR(UTF8_sz)}, {CSTR(UTF8_sz)}, 3, 0}, + {{CSTR(UTF8_sz)}, {CSTR(UTF8_sz)}, 100, 0}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +/* Tests for latin1, for both PAD and NOPAD collations */ +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_latin1_xpad_common[]= +{ + {{CSTR(LATIN1_sz)}, {CSTR(LATIN1_sz)}, 0, 0}, + {{CSTR(LATIN1_sz)}, {CSTR(LATIN1_sz)}, 1, 0}, + {{CSTR(LATIN1_sz)}, {CSTR(LATIN1_sz)}, 2, 0}, + {{CSTR(LATIN1_sz)}, {CSTR(LATIN1_sz)}, 3, 0}, + {{CSTR(LATIN1_sz)}, {CSTR(LATIN1_sz)}, 100, 0}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +/* Tests for utf8 collations that sort "A WITH DIAERESIS" equal to "A" */ +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_utf8mbx_xpad_a_eq_auml[]= +{ + {{CSTR(UTF8_auml "h")}, {CSTR("ah")}, 0, 0}, + {{CSTR(UTF8_auml "h")}, {CSTR("ah")}, 1, 0}, + {{CSTR(UTF8_auml "h")}, {CSTR("ah")}, 2, 0}, + {{CSTR(UTF8_auml "h")}, {CSTR("ah")}, 3, 0}, + {{CSTR(UTF8_auml "h")}, {CSTR("ah")}, 100, 0}, + + {{CSTR(UTF8_auml "h")}, {CSTR("ah ")}, 0, 0}, + {{CSTR(UTF8_auml "h")}, {CSTR("ah ")}, 1, 0}, + {{CSTR(UTF8_auml "h")}, {CSTR("ah ")}, 2, 0}, + {{CSTR(UTF8_auml "h")}, {CSTR("ah ")}, 3, 0}, + {{CSTR(UTF8_auml "h")}, {CSTR("ah ")}, 100, 0}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_utf8mb3_unicode_ci[]= +{ + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 0, 0}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 1, 0}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}/*IF*/, 2, 1}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 3, 0}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 4, 0}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 100, 0}, + + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 0, 0}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 1, -1}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 2, 0}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 3, 0}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 4, 0}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 100, 0}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_utf8mb3_unicode_nopad_ci[]= +{ + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 0, 0}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 1, 0}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}/*IF*/, 2, 1}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 3, 1}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 4, 1}, + {{CSTR("ss")}, {CSTR("s" "\x00" "s")}, 100, 1}, + + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 0, 0}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 1, -1}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 2, -1}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 3, -1}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 4, -1}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 100, -1}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_utf8mb3_danish_ci[]= +{ + {{CSTR("aa")}, {CSTR("")}, 0, 0}, + {{CSTR("aa")}/*CF*/, {CSTR("")}, 1, 1}, + {{CSTR("aa")}, {CSTR("")}, 2, 1}, + {{CSTR("aa")}, {CSTR("")}, 3, 1}, + {{CSTR("aa")}, {CSTR("")}, 100, 1}, + + {{CSTR("aa")}, {CSTR("a")}, 0, 0}, + {{CSTR("aa")}/*CF*/, {CSTR("a")}, 1, 0}, + {{CSTR("aa")}, {CSTR("a")}, 2, 1}, + {{CSTR("aa")}, {CSTR("a")}, 3, 1}, + {{CSTR("aa")}, {CSTR("a")}, 100, 1}, + + {{CSTR("aa")}, {CSTR("aa")}, 0, 0}, + {{CSTR("aa")}/*CF*/, {CSTR("aa")}/*CF*/, 1, 0}, + {{CSTR("aa")}, {CSTR("aa")}, 2, 0}, + {{CSTR("aa")}, {CSTR("aa")}, 3, 0}, + {{CSTR("aa")}, {CSTR("aa")}, 100, 0}, + + {{CSTR("aa")}, {CSTR("\x00" "a")}, 0, 0}, + {{CSTR("aa")}/*CF*/, {CSTR("\x00" "a")}/*IF*/, 1, 1}, + {{CSTR("aa")}, {CSTR("\x00" "a")}, 2, 1}, + {{CSTR("aa")}, {CSTR("\x00" "a")}, 3, 1}, + {{CSTR("aa")}, {CSTR("\x00" "a")}, 100, 1}, + + {{CSTR("aa")}, {CSTR("\x00" "aa")}, 0, 0}, + {{CSTR("aa")}/*CF*/, {CSTR("\x00" "aa")}/*IF*/, 1, 1}, + {{CSTR("aa")}, {CSTR("\x00" "aa")}/*IF*/, 2, 1}, + {{CSTR("aa")}, {CSTR("\x00" "aa")}, 3, 0}, + {{CSTR("aa")}, {CSTR("\x00" "aa")}, 100, 0}, + + {{CSTR("aa")}, {CSTR("a" "\x00" "a")}, 0, 0}, + {{CSTR("aa")}/*CF*/, {CSTR("a" "\x00" "a")}, 1, 0}, + {{CSTR("aa")}, {CSTR("a" "\x00" "a")}/*IF*/, 2, 1}, + {{CSTR("aa")}, {CSTR("a" "\x00" "a")}, 3, 1}, + {{CSTR("aa")}, {CSTR("a" "\x00" "a")}, 100, 1}, + + {{CSTR("aa")}, {CSTR(UTF8_ARING)}, 0, 0}, + {{CSTR("aa")}/*CF*/, {CSTR(UTF8_ARING)}, 1, -1}, + {{CSTR("aa")}, {CSTR(UTF8_ARING)}, 2, 0}, + {{CSTR("aa")}, {CSTR(UTF8_ARING)}, 3, 0}, + {{CSTR("aa")}, {CSTR(UTF8_ARING)}, 100, 0}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_latin1_german2_ci[]= +{ + {{CSTR("ss")}, {CSTR(LATIN1_sz)}, 0, 0}, + {{CSTR("ss")}, {CSTR(LATIN1_sz)}, 1, -1}, + {{CSTR("ss")}, {CSTR(LATIN1_sz)}, 2, 0}, + {{CSTR("ss")}, {CSTR(LATIN1_sz)}, 3, 0}, + {{CSTR("ss")}, {CSTR(LATIN1_sz)}, 100, 0}, + + {{CSTR("ae")}, {CSTR(LATIN1_auml)}, 0, 0}, + {{CSTR("ae")}, {CSTR(LATIN1_auml)}, 1, -1}, + {{CSTR("ae")}, {CSTR(LATIN1_auml)}, 2, 0}, + {{CSTR("ae")}, {CSTR(LATIN1_auml)}, 3, 0}, + {{CSTR("ae")}, {CSTR(LATIN1_auml)}, 100, 0}, + + {{CSTR("ae")}, {CSTR(LATIN1_auml " ")}, 0, 0}, + {{CSTR("ae")}, {CSTR(LATIN1_auml " ")}, 1, -1}, + {{CSTR("ae")}, {CSTR(LATIN1_auml " ")}, 2, 0}, + {{CSTR("ae")}, {CSTR(LATIN1_auml " ")}, 3, 0}, + {{CSTR("ae")}, {CSTR(LATIN1_auml " ")}, 100, 0}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_utf8mbx_german2_ci[]= +{ + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 0, 0}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 1, -1}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 2, 0}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 3, 0}, + {{CSTR("ss")}, {CSTR(UTF8_sz)}, 100, 0}, + + {{CSTR("ae")}, {CSTR(UTF8_auml)}, 0, 0}, + {{CSTR("ae")}, {CSTR(UTF8_auml)}, 1, -1}, + {{CSTR("ae")}, {CSTR(UTF8_auml)}, 2, 0}, + {{CSTR("ae")}, {CSTR(UTF8_auml)}, 3, 0}, + {{CSTR("ae")}, {CSTR(UTF8_auml)}, 100, 0}, + + {{CSTR("ae")}, {CSTR(UTF8_auml " ")}, 0, 0}, + {{CSTR("ae")}, {CSTR(UTF8_auml " ")}, 1, -1}, + {{CSTR("ae")}, {CSTR(UTF8_auml " ")}, 2, 0}, + {{CSTR("ae")}, {CSTR(UTF8_auml " ")}, 3, 0}, + {{CSTR("ae")}, {CSTR(UTF8_auml " ")}, 100, 0}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_mbminlen1_xpad_czech[]= +{ + {{CSTR("c")}, {CSTR("ch")}, 0, 0}, + {{CSTR("c")}, {CSTR("ch")}, 1, 0}, + {{CSTR("c")}, {CSTR("ch")}, 2, -1}, + + {{CSTR("h")}, {CSTR("ch")}, 0, 0}, + {{CSTR("h")}, {CSTR("ch")}, 1, 1}, + {{CSTR("h")}, {CSTR("ch")}, 2, -1}, + + {{CSTR("i")}, {CSTR("ch")}, 0, 0}, + {{CSTR("i")}, {CSTR("ch")}, 1, 1}, + {{CSTR("i")}, {CSTR("ch")}, 2, 1}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +static STRNNCOLLSP_CHAR_PARAM strnncollsp_char_mbminlen2_xpad_common[]= +{ + {{CSTR(UCS2_a)}, {CSTR(UCS2_a)}, 0, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a)}, 1, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a)}, 2, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a)}, 3, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a)}, 100, 0}, + + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp)}, 0, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp)}, 1, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp)}, 2, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp)}, 3, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp)}, 100, 0}, + + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp UCS2_sp)}, 0, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp UCS2_sp)}, 1, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp UCS2_sp)}, 2, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp UCS2_sp)}, 3, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_sp UCS2_sp)}, 100, 0}, + + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_b)}, 0, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_b)}, 1, 0}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_b)}, 2, -1}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_b)}, 3, -1}, + {{CSTR(UCS2_a)}, {CSTR(UCS2_a UCS2_b)}, 100, -1}, + + {{NULL, 0}, {NULL, 0}, 0, 0} +}; + + +static int +strnncollsp_char_one(CHARSET_INFO *cs, const STRNNCOLLSP_CHAR_PARAM *p) +{ + int failed= 0; + char ahex[64], bhex[64]; + int res= cs->coll->strnncollsp_nchars(cs, + (uchar *) p->a.str, p->a.length, + (uchar *) p->b.str, p->b.length, + p->nchars); + str2hex(ahex, sizeof(ahex), p->a.str, p->a.length); + str2hex(bhex, sizeof(bhex), p->b.str, p->b.length); + diag("%-25s %-12s %-12s %3d %7d %7d%s", + cs->cs_name.str, ahex, bhex, (int) p->nchars, p->res, res, + eqres(res, p->res) ? "" : " FAILED"); + if (!eqres(res, p->res)) + { + failed++; + } + else + { + /* Test in reverse order */ + res= cs->coll->strnncollsp_nchars(cs, + (uchar *) p->b.str, p->b.length, + (uchar *) p->a.str, p->a.length, + p->nchars); + if (!eqres(res, -p->res)) + { + diag("Comparison in reverse order failed. Expected %d, got %d", + -p->res, res); + failed++; + } + } + return failed; +} + + +static int +strnncollsp_char(const char *collation, const STRNNCOLLSP_CHAR_PARAM *param) +{ + int failed= 0; + const STRNNCOLLSP_CHAR_PARAM *p; + CHARSET_INFO *cs= get_charset_by_name(collation, MYF(0)); + + if (!cs) + { + diag("get_charset_by_name() failed"); + return 1; + } + + diag("%-25s %-12s %-12s %-3s %7s %7s", + "Collation", "a", "b", "Nch", "ExpSign", "Actual"); + + for (p= param; p->a.str; p++) + { + failed+= strnncollsp_char_one(cs, p); + } + + return failed; +} + + +static int +strnncollsp_char_mbminlen1(const char *collation, + const STRNNCOLLSP_CHAR_PARAM *specific) +{ + int failed= 0; + failed+= strnncollsp_char(collation, strnncollsp_char_mbminlen1_xpad_common); + if (specific) + failed+= strnncollsp_char(collation, specific); + return failed; +} + + +static int +strnncollsp_char_mbminlen2(const char *collation, + const STRNNCOLLSP_CHAR_PARAM *specific) +{ + int failed= 0; + failed+= strnncollsp_char(collation, strnncollsp_char_mbminlen2_xpad_common); + if (specific) + failed+= strnncollsp_char(collation, specific); + return failed; +} + + +static int +strnncollsp_char_latin1(const char *collation, + const STRNNCOLLSP_CHAR_PARAM *specific) +{ + int failed= 0; + failed+= strnncollsp_char(collation, strnncollsp_char_mbminlen1_xpad_common); + failed+= strnncollsp_char(collation, strnncollsp_char_latin1_xpad_common); + if (specific) + failed+= strnncollsp_char(collation, specific); + return failed; +} + + +static int +strnncollsp_char_utf8mbx(const char *collation, + const STRNNCOLLSP_CHAR_PARAM *specific) +{ + int failed= 0; + failed+= strnncollsp_char(collation, strnncollsp_char_mbminlen1_xpad_common); + failed+= strnncollsp_char(collation, strnncollsp_char_utf8mbx_xpad_common); + + if (!strstr(collation, "_bin") && + !strstr(collation, "_german2") && + !strstr(collation, "_danish")) + failed+= strnncollsp_char(collation, + strnncollsp_char_utf8mbx_xpad_a_eq_auml); + if (specific) + failed+= strnncollsp_char(collation, specific); + return failed; +} + + +static int +test_strnncollsp_char() +{ + int failed= 0; + failed+= strnncollsp_char_latin1("latin1_swedish_ci", NULL); + failed+= strnncollsp_char_latin1("latin1_swedish_nopad_ci", NULL); + failed+= strnncollsp_char_latin1("latin1_bin", NULL); + failed+= strnncollsp_char_latin1("latin1_nopad_bin", NULL); + failed+= strnncollsp_char_latin1("latin1_german2_ci", + strnncollsp_char_latin1_german2_ci); + +#ifdef HAVE_CHARSET_cp1250 + failed+= strnncollsp_char_mbminlen1("cp1250_czech_cs", + strnncollsp_char_mbminlen1_xpad_czech); +#endif + +#ifdef HAVE_CHARSET_latin2 + failed+= strnncollsp_char_mbminlen1("latin2_czech_cs", + strnncollsp_char_mbminlen1_xpad_czech); +#endif + +#ifdef HAVE_CHARSET_tis620 + failed+= strnncollsp_char_mbminlen1("tis620_thai_ci", NULL); +#endif + +#ifdef HAVE_CHARSET_big5 + failed+= strnncollsp_char_mbminlen1("big5_chinese_ci", NULL); + failed+= strnncollsp_char_mbminlen1("big5_chinese_nopad_ci", NULL); + failed+= strnncollsp_char_mbminlen1("big5_bin", NULL); + failed+= strnncollsp_char_mbminlen1("big5_nopad_bin", NULL); +#endif + + failed+= strnncollsp_char_utf8mbx("utf8mb3_general_ci", NULL); + failed+= strnncollsp_char_utf8mbx("utf8mb3_general_nopad_ci", NULL); + failed+= strnncollsp_char_utf8mbx("utf8mb3_bin", NULL); + failed+= strnncollsp_char_utf8mbx("utf8mb3_nopad_bin", NULL); + + failed+= strnncollsp_char_utf8mbx("utf8mb3_unicode_ci", + strnncollsp_char_utf8mb3_unicode_ci); + failed+= strnncollsp_char_utf8mbx("utf8mb3_unicode_nopad_ci", + strnncollsp_char_utf8mb3_unicode_nopad_ci); + failed+= strnncollsp_char_utf8mbx("utf8mb3_danish_ci", + strnncollsp_char_utf8mb3_danish_ci); + failed+= strnncollsp_char_utf8mbx("utf8mb3_german2_ci", + strnncollsp_char_utf8mbx_german2_ci); + failed+= strnncollsp_char_utf8mbx("utf8mb3_czech_ci", + strnncollsp_char_mbminlen1_xpad_czech); + +#ifdef HAVE_CHARSET_ucs2 + failed+= strnncollsp_char_mbminlen2("ucs2_general_ci", NULL); + failed+= strnncollsp_char_mbminlen2("ucs2_general_nopad_ci", NULL); + failed+= strnncollsp_char_mbminlen2("ucs2_bin", NULL); + failed+= strnncollsp_char_mbminlen2("ucs2_nopad_bin", NULL); + failed+= strnncollsp_char_mbminlen2("ucs2_unicode_ci", NULL); + failed+= strnncollsp_char_mbminlen2("ucs2_unicode_nopad_ci", NULL); +#endif + + return failed; +} + + +int main(int ac, char **av) +{ + size_t i, failed= 0; + + MY_INIT(av[0]); + + plan(4); + diag("Testing my_like_range_xxx() functions"); + + for (i= 0; i < array_elements(charset_list); i++) + { + CHARSET_INFO *cs= charset_list[i]; + if (test_like_range_for_charset(cs, "abc%", 4)) + { + ++failed; + diag("Failed for %s", cs->coll_name.str); + } + } + ok(failed == 0, "Testing my_like_range_xxx() functions"); + + diag("my_ci_strnncollsp()"); + failed= test_strcollsp(); + ok(failed == 0, "Testing my_ci_strnncollsp()"); + + diag("Testing min_str() and max_str()"); + failed= test_minmax_str(); + ok(failed == 0, "Testing min_str() and max_str() functions"); + + diag("Testing cs->coll->strnncollsp_char()"); + failed= test_strnncollsp_char(); + ok(failed == 0, "Testing cs->coll->strnncollsp_char()"); + + my_end(0); + + return exit_status(); +} diff --git a/unittest/unit.pl b/unittest/unit.pl new file mode 100644 index 00000000..dc72a57d --- /dev/null +++ b/unittest/unit.pl @@ -0,0 +1,135 @@ +#!/usr/bin/env perl +# Copyright (c) 2006 MySQL AB, 2009, 2010 Sun Microsystems, Inc. +# Use is subject to license terms. +# +# 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-1335 USA + +use File::Find; +use Getopt::Long; + +use strict; + +sub run_cmd (@); + +my %dispatch = ( + "run" => \&run_cmd, +); + +=head1 NAME + +unit - Run unit tests in directory + +=head1 SYNOPSIS + + unit [--[no]big] [--[no]verbose] run [tests to run] + +=cut + +my $big= $ENV{'MYTAP_CONFIG'} eq 'big'; + +my $opt_verbose; +my $result = GetOptions ( + "big!" => \$big, + "verbose!" => \$opt_verbose, +); + +$ENV{'MYTAP_CONFIG'} = $big ? 'big' : ''; + +my $cmd = shift; + +if (defined $cmd && exists $dispatch{$cmd}) { + $dispatch{$cmd}->(@ARGV); +} else { + print "Unknown command", (defined $cmd ? " $cmd" : ""), ".\n"; + print "Available commands are: ", join(", ", keys %dispatch), "\n"; +} + +=head2 run + +Run all unit tests in the current directory and all subdirectories. + +=cut + +BEGIN { + # Test::Harness have been extensively rewritten in newer perl + # versions and is now just a backward compatibility wrapper + # (with a bug causing the HARNESS_PERL_SWITCHES to be mangled) + # Prefer to use TAP::Harness directly if available + if (eval "use TAP::Harness; 1") { + eval 'sub NEW_HARNESS { 1 }'; + warn "using TAP::Harness"; + } else { + eval "use Test::Harness; 1" or die "couldn't find Test::Harness!"; + eval 'sub NEW_HARNESS { 0 }'; + } +} + +sub _find_test_files (@) { + my @dirs = @_; + my @files; + find sub { + $File::Find::prune = 1 if /^(SCCS|\.libs)$/; + push(@files, $File::Find::name) if -x _ && (/-t\z/ || /-t\.exe\z/); + }, @dirs; + return @files; +} + +sub run_cmd (@) { + my @files; + + # If no directories were supplied, we add all directories in the + # current directory except 'mytap' since it is not part of the + # test suite. + if (@_ == 0) { + # Ignore these directories + my @ignore = qw(mytap SCCS); + + # Build an expression from the directories above that tests if a + # directory should be included in the list or not. + my $ignore = join(' && ', map { '$_ ne ' . "'$_'"} @ignore); + + # Open and read the directory. Filter out all files, hidden + # directories, and directories named above. + opendir(DIR, ".") or die "Cannot open '.': $!\n"; + @_ = grep { -d $_ && $_ !~ /^\..*/ && eval $ignore } readdir(DIR); + closedir(DIR); + } + + print "Running tests: @_\n"; + + foreach my $name (@_) { + push(@files, _find_test_files $name) if -d $name; + push(@files, $name) if -f $name; + } + + if (@files > 0) { + # Removing the first './' from the file names + foreach (@files) { s!^\./!! } + + if (NEW_HARNESS()) + { + my %args = ( exec => [ ], verbosity => $opt_verbose ); + my $harness = TAP::Harness->new( \%args ); + $harness->runtests(@files); + } + else + { + $ENV{'HARNESS_VERBOSE'} = $opt_verbose; + $ENV{'HARNESS_PERL_SWITCHES'} .= ' -e "exec @ARGV"'; + $ENV{'HARNESS_OPTIONS'}="j4"; + $Test::Harness::Timer = 1; + runtests(@files); + } + } +} |