summaryrefslogtreecommitdiffstats
path: root/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'unittest')
-rw-r--r--unittest/README.txt53
-rw-r--r--unittest/embedded/CMakeLists.txt20
-rw-r--r--unittest/embedded/test-connect.cc78
-rw-r--r--unittest/examples/CMakeLists.txt16
-rw-r--r--unittest/examples/core-t.c34
-rw-r--r--unittest/examples/no_plan-t.c40
-rw-r--r--unittest/examples/simple-t.c53
-rw-r--r--unittest/examples/skip-t.c29
-rw-r--r--unittest/examples/skip_all-t.c40
-rw-r--r--unittest/examples/todo-t.c36
-rw-r--r--unittest/json_lib/CMakeLists.txt22
-rw-r--r--unittest/json_lib/json_lib-t.c186
-rw-r--r--unittest/my_decimal/CMakeLists.txt22
-rw-r--r--unittest/my_decimal/my_decimal-t.cc102
-rw-r--r--unittest/mysys/CMakeLists.txt28
-rw-r--r--unittest/mysys/aes-t.c158
-rw-r--r--unittest/mysys/base64-t.c99
-rw-r--r--unittest/mysys/bitmap-t.c541
-rw-r--r--unittest/mysys/byte_order-t.c102
-rw-r--r--unittest/mysys/crc32-t.c69
-rw-r--r--unittest/mysys/dynstring-t.c74
-rw-r--r--unittest/mysys/lf-t.c199
-rw-r--r--unittest/mysys/ma_dyncol-t.c881
-rw-r--r--unittest/mysys/my_atomic-t.c131
-rw-r--r--unittest/mysys/my_delete-t.c57
-rw-r--r--unittest/mysys/my_getopt-t.c389
-rw-r--r--unittest/mysys/my_malloc-t.c44
-rw-r--r--unittest/mysys/my_rdtsc-t.c230
-rw-r--r--unittest/mysys/my_vsnprintf-t.c214
-rw-r--r--unittest/mysys/queues-t.c139
-rw-r--r--unittest/mysys/stacktrace-t.c69
-rw-r--r--unittest/mysys/thr_template.c73
-rw-r--r--unittest/mysys/waiting_threads-t.c286
-rw-r--r--unittest/mytap/CMakeLists.txt17
-rw-r--r--unittest/mytap/Doxyfile1156
-rw-r--r--unittest/mytap/t/basic-t.c34
-rw-r--r--unittest/mytap/tap.c705
-rw-r--r--unittest/mytap/tap.h314
-rw-r--r--unittest/sql/CMakeLists.txt36
-rw-r--r--unittest/sql/dummy_builtins.cc26
-rw-r--r--unittest/sql/explain_filename-t.cc166
-rw-r--r--unittest/sql/mf_iocache-t.cc474
-rw-r--r--unittest/sql/my_apc-t.cc229
-rw-r--r--unittest/sql/my_json_writer-t.cc128
-rw-r--r--unittest/strings/CMakeLists.txt3
-rw-r--r--unittest/strings/json-t.c103
-rw-r--r--unittest/strings/strings-t.c1545
-rw-r--r--unittest/unit.pl135
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= &params[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);
+ }
+ }
+}