#At file:///export/home/didrik/repo/trunk-thd-item/ based on revid:tor.didriksen@stripped
3563 Tor Didriksen 2011-01-28
Bug #59794 Enable unit testing of classes which depend on a working THD environment
Large parts of the codebase depend on a THD instance in order to be tested.
The THD needs to be modified, so it can be used in unit tests.
Some global initialization also needs to be done.
@ include/my_global.h
MY_INTnn_NUM_DECIMAL_DIGITS are always used in unsigned context.
@ sql/mysqld.cc
init_thread_environment() must be callable by unit tests.
@ sql/mysqld.h
init_thread_environment() must be callable by unit tests.
@ sql/sql_class.cc
Disable plugins for unit tests.
@ sql/sql_class.h
Disable plugins for unit tests.
@ sql/sql_plugin.cc
Disable plugins for unit tests.
@ unittest/gunit/CMakeLists.txt
Add new linkage rules for unit tests which depend on server libraries.
@ unittest/gunit/item-t.cc
New unit test.
added:
unittest/gunit/item-t.cc
modified:
include/my_global.h
sql/mysqld.cc
sql/mysqld.h
sql/sql_class.cc
sql/sql_class.h
sql/sql_plugin.cc
unittest/gunit/CMakeLists.txt
=== modified file 'include/my_global.h'
--- a/include/my_global.h 2011-01-11 09:09:21 +0000
+++ b/include/my_global.h 2011-01-28 13:37:15 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB, 2009 Sun Microsystems, Inc
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1377,10 +1377,10 @@ do { doubleget_union _tmp; \
#endif
/* Length of decimal number represented by INT32. */
-#define MY_INT32_NUM_DECIMAL_DIGITS 11
+#define MY_INT32_NUM_DECIMAL_DIGITS 11U
/* Length of decimal number represented by INT64. */
-#define MY_INT64_NUM_DECIMAL_DIGITS 21
+#define MY_INT64_NUM_DECIMAL_DIGITS 21U
/* Define some useful general macros (should be done after all headers). */
#if !defined(max)
=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc 2011-01-17 09:52:59 +0000
+++ b/sql/mysqld.cc 2011-01-28 13:37:15 +0000
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -944,7 +944,6 @@ static int get_options(int *argc_ptr, ch
static bool add_terminator(DYNAMIC_ARRAY *options);
extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *);
static void set_server_version(void);
-static int init_thread_environment();
static char *get_relative_path(const char *path);
static int fix_paths(void);
void handle_connections_sockets();
@@ -3551,7 +3550,7 @@ You should consider changing lower_case_
}
-static int init_thread_environment()
+int init_thread_environment()
{
mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST);
=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h 2011-01-14 13:42:41 +0000
+++ b/sql/mysqld.h 2011-01-28 13:37:15 +0000
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+/* 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
@@ -72,6 +72,7 @@ bool one_thread_per_connection_end(THD *
void flush_thread_cache();
void refresh_status(THD *thd);
bool is_secure_file_path(char *path);
+int init_thread_environment();
extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info;
extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ;
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2011-01-27 15:31:18 +0000
+++ b/sql/sql_class.cc 2011-01-28 13:37:15 +0000
@@ -498,7 +498,7 @@ bool Drop_table_error_handler::handle_co
}
-THD::THD()
+THD::THD(bool enable_plugins)
:Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION,
/* statement id */ 0),
rli_fake(0),
@@ -527,6 +527,7 @@ THD::THD()
#if defined(ENABLED_DEBUG_SYNC)
debug_sync_control(0),
#endif /* defined(ENABLED_DEBUG_SYNC) */
+ m_enable_plugins(enable_plugins),
main_warning_info(0)
{
ulong tmp;
@@ -928,7 +929,8 @@ extern "C" THD *_current_thd_noinline(
void THD::init(void)
{
mysql_mutex_lock(&LOCK_global_system_variables);
- plugin_thdvar_init(this);
+ if (m_enable_plugins)
+ plugin_thdvar_init(this);
/*
variables= global_system_variables above has reset
variables.pseudo_thread_id to 0. We need to correct it here to
@@ -1102,7 +1104,8 @@ THD::~THD()
mdl_context.destroy();
ha_close_connection(this);
mysql_audit_release(this);
- plugin_thdvar_cleanup(this);
+ if (m_enable_plugins)
+ plugin_thdvar_cleanup(this);
DBUG_PRINT("info", ("freeing security context"));
main_security_ctx.destroy();
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2011-01-27 15:31:18 +0000
+++ b/sql/sql_class.h 2011-01-28 13:37:15 +0000
@@ -2233,7 +2233,11 @@ public:
/* Debug Sync facility. See debug_sync.cc. */
struct st_debug_sync_control *debug_sync_control;
#endif /* defined(ENABLED_DEBUG_SYNC) */
- THD();
+
+ // We don't want to load/unload plugins for unit tests.
+ bool m_enable_plugins;
+
+ THD(bool enable_plugins= true);
~THD();
void init(void);
=== modified file 'sql/sql_plugin.cc'
--- a/sql/sql_plugin.cc 2010-12-06 13:12:51 +0000
+++ b/sql/sql_plugin.cc 2011-01-28 13:37:15 +0000
@@ -1,4 +1,4 @@
-/* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2005, 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
@@ -1039,6 +1039,9 @@ void plugin_unlock_list(THD *thd, plugin
LEX *lex= thd ? thd->lex : 0;
DBUG_ENTER("plugin_unlock_list");
DBUG_ASSERT(list);
+ if (count == 0)
+ DBUG_VOID_RETURN;
+
mysql_mutex_lock(&LOCK_plugin);
while (count--)
intern_plugin_unlock(lex, *list++);
=== modified file 'unittest/gunit/CMakeLists.txt'
--- a/unittest/gunit/CMakeLists.txt 2011-01-14 09:29:11 +0000
+++ b/unittest/gunit/CMakeLists.txt 2011-01-28 13:37:15 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Sun Microsystems,Inc
+# Copyright (c) 2009, 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
@@ -122,7 +122,7 @@ IF(NOT GTEST_FOUND)
FIND_PROGRAM(WGET_EXECUTABLE wget)
MARK_AS_ADVANCED(WGET_EXECUTABLE)
IF(WGET_EXECUTABLE)
- EXECUTE_PROCESS(COMMAND ${WGET_EXECUTABLE} -T 30 ${GTEST_DOWNLOAD_URL}
+ EXECUTE_PROCESS(COMMAND ${WGET_EXECUTABLE} -T 30 ${GTEST_DOWNLOAD_URL}
WORKING_DIRECTORY ${DOWNLOAD_ROOT} RESULT_VARIABLE ERR)
IF(ERR EQUAL 0)
SET(DOWNLOAD_SUCCEEDED 1)
@@ -218,6 +218,11 @@ SET(TESTS
thread_utils
)
+# Add tests (link them with gunit library and the server libraries)
+SET(SERVER_TESTS
+ item
+)
+
FOREACH(test ${TESTS})
ADD_EXECUTABLE(${test}-t ${test}-t.cc)
TARGET_LINK_LIBRARIES(${test}-t gunit sqlgunitlib strings dbug regex)
@@ -226,3 +231,17 @@ FOREACH(test ${TESTS})
ENDIF()
ADD_TEST(${test} ${test}-t)
ENDFOREACH()
+
+FOREACH(test ${SERVER_TESTS})
+ IF(WIN32)
+ ADD_EXECUTABLE(${test}-t ${test}-t.cc ../../sql/nt_servc.cc)
+ ELSE()
+ ADD_EXECUTABLE(${test}-t ${test}-t.cc)
+ ENDIF()
+ TARGET_LINK_LIBRARIES(${test}-t sql binlog rpl master slave sql)
+ TARGET_LINK_LIBRARIES(${test}-t gunit sqlgunitlib strings dbug regex)
+ IF (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro")
+ SET_TARGET_PROPERTIES(${test}-t PROPERTIES LINK_FLAGS "-library=stlport4")
+ ENDIF()
+ ADD_TEST(${test} ${test}-t)
+ENDFOREACH()
=== added file 'unittest/gunit/item-t.cc'
--- a/unittest/gunit/item-t.cc 1970-01-01 00:00:00 +0000
+++ b/unittest/gunit/item-t.cc 2011-01-28 13:37:15 +0000
@@ -0,0 +1,170 @@
+/* Copyright (c) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+// First include (the generated) my_config.h, to get correct platform defines,
+// then gtest.h (before any other MySQL headers), to avoid min() macros etc ...
+#include "my_config.h"
+#include <gtest/gtest.h>
+
+#include "item.h"
+#include "sql_class.h"
+#include "rpl_handler.h" // delegates_init()
+
+namespace {
+
+class ItemTest : public ::testing::Test
+{
+protected:
+ /*
+ This is the part of the server global things which have to be initialized
+ for this (very simple) unit test. Presumably the list will grow once
+ we start writing tests for more advanced classes.
+ TODO: Move to a common library.
+ */
+ static void SetUpTestCase()
+ {
+ init_thread_environment();
+ randominit(&sql_rand, 0, 0);
+ xid_cache_init();
+ delegates_init();
+ }
+
+ static void TearDownTestCase()
+ {
+ delegates_destroy();
+ xid_cache_free();
+ }
+
+ ItemTest() : m_thd(NULL) {}
+
+ virtual void SetUp()
+ {
+ m_thd= new THD(false);
+ m_thd->thread_stack= (char*) &m_thd;
+ m_thd->store_globals();
+ }
+
+ virtual void TearDown()
+ {
+ m_thd->cleanup_after_query();
+ delete m_thd;
+ }
+
+private:
+ THD *m_thd;
+};
+
+
+/**
+ This is a simple mock Field class, which verifies that store() is called.
+ TODO: Introduce Google Mock to simplify writing of mock classes.
+*/
+class Mock_field_long : public Field_long
+{
+public:
+ Mock_field_long(uint32 lenght, longlong expected_value)
+ : Field_long(0, // ptr_arg
+ lenght, // len_arg
+ NULL, // null_ptr_arg
+ 0, // null_bit_arg
+ Field::NONE, // unireg_check_arg
+ 0, // field_name_arg
+ false, // zero_arg
+ false), // unsigned_arg
+ m_store_called(0),
+ m_expected_value(expected_value)
+ {}
+
+ // The destructor verifies that store() has been called.
+ virtual ~Mock_field_long()
+ {
+ EXPECT_EQ(1, m_store_called);
+ }
+
+ /*
+ This is the only member function we need to override.
+ We expect it to be called with specific arguments.
+ */
+ virtual int store(longlong nr, bool unsigned_val)
+ {
+ EXPECT_EQ(m_expected_value, nr);
+ EXPECT_FALSE(unsigned_val);
+ ++m_store_called;
+ return 0;
+ }
+
+private:
+ int m_store_called;
+ longlong m_expected_value;
+};
+
+
+TEST_F(ItemTest, item_int)
+{
+ const int32 val= 42;
+ char stringbuf[10];
+ (void) my_snprintf(stringbuf, sizeof(stringbuf), "%d", val);
+
+ // An Item expects to be owned by current_thd->free_list,
+ // so allocate with new, and do not delete it.
+ Item_int *item_int= new Item_int(val);
+
+ EXPECT_EQ(Item::INT_ITEM, item_int->type());
+ EXPECT_EQ(INT_RESULT, item_int->result_type());
+ EXPECT_EQ(MYSQL_TYPE_LONGLONG, item_int->field_type());
+ EXPECT_EQ(val, item_int->val_int());
+ EXPECT_DOUBLE_EQ((double) val, item_int->val_real());
+ EXPECT_TRUE(item_int->basic_const_item());
+
+ my_decimal decimal_val;
+ EXPECT_EQ(&decimal_val, item_int->val_decimal(&decimal_val));
+
+ String string_val;
+ EXPECT_EQ(&string_val, item_int->val_str(&string_val));
+ EXPECT_STREQ(stringbuf, string_val.c_ptr_safe());
+
+ {
+ // New scope, since we have EXPECT_EQ in the destructor as well.
+ Mock_field_long field_val(item_int->max_length, val);
+ EXPECT_EQ(0, item_int->save_in_field(&field_val, true));
+ }
+
+ Item *clone= item_int->clone_item();
+ EXPECT_TRUE(item_int->eq(clone, true));
+ EXPECT_TRUE(item_int->eq(item_int, true));
+
+ String print_val;
+ item_int->print(&print_val, QT_ORDINARY);
+ EXPECT_STREQ(stringbuf, print_val.c_ptr_safe());
+
+ const uint precision= item_int->decimal_precision();
+ EXPECT_EQ(MY_INT32_NUM_DECIMAL_DIGITS, precision);
+
+ item_int->neg();
+ EXPECT_EQ(-val, item_int->val_int());
+ EXPECT_EQ(precision - 1, item_int->decimal_precision());
+
+ // Functions inherited from parent class(es).
+ const table_map tmap= 0;
+ EXPECT_EQ(tmap, item_int->used_tables());
+
+ /*
+ TODO: There are about 100 member functions in Item.
+ Figure out which ones are relevant for unit testing here.
+ */
+}
+
+}
Attachment: [text/bzr-bundle] bzr/tor.didriksen@oracle.com-20110128133715-g4ghnpwwcfuw1y5u.bundle