From: Tor Didriksen Date: May 13 2011 9:42am Subject: bzr push into mysql-trunk branch (tor.didriksen:3080 to 3081) Bug#11788245 List-Archive: http://lists.mysql.com/commits/137301 X-Bug: 11788245 Message-Id: <20110513094215.EBEC23793@atum07.norway.sun.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 3081 Tor Didriksen 2011-05-13 Bug #11788245 - 60110 DO MORE THD AND GLOBAL INITIALIZATION FOR UNIT TESTING Move server/THD initialization to separate utility classes in namespace my_testing. Add new test as an example of how to use the utility classes. @ unittest/gunit/CMakeLists.txt Add test_utils.cc @ unittest/gunit/item-t.cc Use new utility classes. @ unittest/gunit/opt_range-t.cc Another example of using the utility classes. @ unittest/gunit/test_utils.cc Code moved from Item test, into new utility classes. @ unittest/gunit/test_utils.h Code moved from Item test, into new utility classes. added: unittest/gunit/opt_range-t.cc unittest/gunit/test_utils.cc unittest/gunit/test_utils.h modified: unittest/gunit/CMakeLists.txt unittest/gunit/item-t.cc 3080 Bjorn Munch 2011-05-13 fix Tree name after accidental push modified: .bzr-mysql/default.conf === modified file 'unittest/gunit/CMakeLists.txt' --- a/unittest/gunit/CMakeLists.txt 2011-04-13 11:31:44 +0000 +++ b/unittest/gunit/CMakeLists.txt 2011-05-13 09:36:13 +0000 @@ -219,6 +219,7 @@ SET(TESTS # Add tests (link them with gunit library and the server libraries) SET(SERVER_TESTS item + opt_range ) FOREACH(test ${TESTS}) @@ -232,9 +233,9 @@ ENDFOREACH() FOREACH(test ${SERVER_TESTS}) IF(WIN32) - ADD_EXECUTABLE(${test}-t ${test}-t.cc ../../sql/nt_servc.cc) + ADD_EXECUTABLE(${test}-t ${test}-t.cc test_utils.cc ../../sql/nt_servc.cc) ELSE() - ADD_EXECUTABLE(${test}-t ${test}-t.cc) + ADD_EXECUTABLE(${test}-t ${test}-t.cc test_utils.cc) ENDIF() TARGET_LINK_LIBRARIES(${test}-t sql binlog rpl master slave sql) TARGET_LINK_LIBRARIES(${test}-t gunit sqlgunitlib strings dbug regex mysys) === modified file 'unittest/gunit/item-t.cc' --- a/unittest/gunit/item-t.cc 2011-03-18 12:25:56 +0000 +++ b/unittest/gunit/item-t.cc 2011-05-13 09:36:13 +0000 @@ -18,112 +18,42 @@ #include "my_config.h" #include +#include "test_utils.h" + #include "item.h" #include "sql_class.h" -#include "rpl_handler.h" // delegates_init() namespace { -/* - A mock error handler for error_handler_hook. -*/ -uint expected_error= 0; -extern "C" void test_error_handler_hook(uint err, const char *str, myf MyFlags) -{ - EXPECT_EQ(expected_error, err) << str; -} - - -/** - A mock error handler which registers itself with the THD in the CTOR, - and unregisters in the DTOR. The function handle_condition() will - verify that it is called with the expected error number. - The DTOR will verify that handle_condition() has actually been called. -*/ -class Mock_error_handler : public Internal_error_handler -{ -public: - Mock_error_handler(THD *thd, uint expected_error) - : m_thd(thd), - m_expected_error(expected_error), - m_handle_called(0) - { - thd->push_internal_handler(this); - } - - virtual ~Mock_error_handler() - { - // Strange Visual Studio bug: have to store 'this' in local variable. - Internal_error_handler *me= this; - EXPECT_EQ(me, m_thd->pop_internal_handler()); - EXPECT_GE(m_handle_called, 0); - } - - virtual bool handle_condition(THD *thd, - uint sql_errno, - const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, - const char* msg, - MYSQL_ERROR ** cond_hdl) - { - EXPECT_EQ(sql_errno, m_expected_error); - ++m_handle_called; - return true; - } -private: - THD *m_thd; - uint m_expected_error; - int m_handle_called; -}; - +using my_testing::Server_initializer; +using my_testing::Mock_error_handler; 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() { - static char *my_name= strdup(my_progname); - char *argv[] = { my_name, 0 }; - set_remaining_args(1, argv); - init_common_variables(); - my_init_signals(); - randominit(&sql_rand, 0, 0); - xid_cache_init(); - delegates_init(); - error_handler_hook= test_error_handler_hook; + Server_initializer::SetUpTestCase(); } static void TearDownTestCase() { - delegates_destroy(); - xid_cache_free(); + Server_initializer::TearDownTestCase(); } - ItemTest() : m_thd(NULL) {} - virtual void SetUp() { - expected_error= 0; - m_thd= new THD(false); - THD *stack_thd= m_thd; - m_thd->thread_stack= (char*) &stack_thd; - m_thd->store_globals(); - lex_start(m_thd); + initializer.SetUp(); } virtual void TearDown() { - m_thd->cleanup_after_query(); - delete m_thd; + initializer.TearDown(); } - THD *m_thd; + THD *thd() { return initializer.thd(); } + + Server_initializer initializer; }; @@ -239,7 +169,7 @@ TEST_F(ItemTest, ItemFuncDesDecrypt) Item_func_des_decrypt *item_decrypt= new Item_func_des_decrypt(item_two, item_one); - EXPECT_FALSE(item_decrypt->fix_fields(m_thd, NULL)); + EXPECT_FALSE(item_decrypt->fix_fields(thd(), NULL)); EXPECT_EQ(length, item_one->max_length); EXPECT_EQ(length, item_two->max_length); EXPECT_LE(item_decrypt->max_length, length); @@ -256,9 +186,9 @@ TEST_F(ItemTest, ItemFuncIntDivOverflow) Item_float *divisor= new Item_float(divisor_str, sizeof(divisor_str)); Item_func_int_div* quotient= new Item_func_int_div(dividend, divisor); - Mock_error_handler error_handler(m_thd, ER_TRUNCATED_WRONG_VALUE); - EXPECT_FALSE(quotient->fix_fields(m_thd, NULL)); - expected_error= ER_DATA_OUT_OF_RANGE; + Mock_error_handler error_handler(thd(), ER_TRUNCATED_WRONG_VALUE); + EXPECT_FALSE(quotient->fix_fields(thd(), NULL)); + initializer.set_expected_error(ER_DATA_OUT_OF_RANGE); quotient->val_int(); } @@ -272,8 +202,8 @@ TEST_F(ItemTest, ItemFuncIntDivUnderflow Item_float *divisor= new Item_float(divisor_str, sizeof(divisor_str)); Item_func_int_div* quotient= new Item_func_int_div(dividend, divisor); - Mock_error_handler error_handler(m_thd, ER_TRUNCATED_WRONG_VALUE); - EXPECT_FALSE(quotient->fix_fields(m_thd, NULL)); + Mock_error_handler error_handler(thd(), ER_TRUNCATED_WRONG_VALUE); + EXPECT_FALSE(quotient->fix_fields(thd(), NULL)); EXPECT_EQ(0, quotient->val_int()); } @@ -291,8 +221,8 @@ TEST_F(ItemTest, ItemFuncSetUserVar) LEX_STRING var_name= { C_STRING_WITH_LEN("a") }; Item_func_set_user_var *user_var= new Item_func_set_user_var(var_name, item_str); - EXPECT_FALSE(user_var->set_entry(m_thd, true)); - EXPECT_FALSE(user_var->fix_fields(m_thd, NULL)); + EXPECT_FALSE(user_var->set_entry(thd(), true)); + EXPECT_FALSE(user_var->fix_fields(thd(), NULL)); EXPECT_EQ(val1, user_var->val_int()); my_decimal decimal; @@ -323,7 +253,7 @@ TEST_F(ItemTest, OutOfMemory) EXPECT_EQ(null_item, item); DBUG_SET("+d,simulate_out_of_memory"); - item= new (m_thd->mem_root) Item_int(42); + item= new (thd()->mem_root) Item_int(42); EXPECT_EQ(null_item, item); #endif } @@ -337,7 +267,7 @@ TEST_F(ItemTest, ItemFuncXor) Item_func_xor *item_xor= new Item_func_xor(item_zero, item_one_a); - EXPECT_FALSE(item_xor->fix_fields(m_thd, NULL)); + EXPECT_FALSE(item_xor->fix_fields(thd(), NULL)); EXPECT_EQ(1, item_xor->val_int()); EXPECT_EQ(1U, item_xor->decimal_precision()); @@ -346,7 +276,7 @@ TEST_F(ItemTest, ItemFuncXor) Item_func_xor *item_xor_same= new Item_func_xor(item_one_a, item_one_b); - EXPECT_FALSE(item_xor_same->fix_fields(m_thd, NULL)); + EXPECT_FALSE(item_xor_same->fix_fields(thd(), NULL)); EXPECT_EQ(0, item_xor_same->val_int()); EXPECT_FALSE(item_xor_same->val_bool()); EXPECT_FALSE(item_xor_same->is_null()); @@ -355,8 +285,8 @@ TEST_F(ItemTest, ItemFuncXor) item_xor->print(&print_buffer, QT_ORDINARY); EXPECT_STREQ("(0 xor 1)", print_buffer.c_ptr_safe()); - Item *neg_xor= item_xor->neg_transformer(m_thd); - EXPECT_FALSE(neg_xor->fix_fields(m_thd, NULL)); + Item *neg_xor= item_xor->neg_transformer(thd()); + EXPECT_FALSE(neg_xor->fix_fields(thd(), NULL)); EXPECT_EQ(0, neg_xor->val_int()); EXPECT_DOUBLE_EQ(0.0, neg_xor->val_real()); EXPECT_FALSE(neg_xor->val_bool()); @@ -368,7 +298,7 @@ TEST_F(ItemTest, ItemFuncXor) Item_func_xor *item_xor_null= new Item_func_xor(item_zero, new Item_null()); - EXPECT_FALSE(item_xor_null->fix_fields(m_thd, NULL)); + EXPECT_FALSE(item_xor_null->fix_fields(thd(), NULL)); EXPECT_EQ(0, item_xor_null->val_int()); EXPECT_TRUE(item_xor_null->is_null()); === added file 'unittest/gunit/opt_range-t.cc' --- a/unittest/gunit/opt_range-t.cc 1970-01-01 00:00:00 +0000 +++ b/unittest/gunit/opt_range-t.cc 2011-05-13 09:36:13 +0000 @@ -0,0 +1,150 @@ +/* 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 + +#include "test_utils.h" + +#include "opt_range.cc" + +namespace { + +using my_testing::Server_initializer; + +class SelArgTest : public ::testing::Test +{ +protected: + static void SetUpTestCase() + { + Server_initializer::SetUpTestCase(); + } + + static void TearDownTestCase() + { + Server_initializer::TearDownTestCase(); + } + + SelArgTest() + { + memset(&m_opt_param, 0, sizeof(m_opt_param)); + } + + virtual void SetUp() + { + initializer.SetUp(); + m_opt_param.thd= thd(); + m_opt_param.mem_root= &m_alloc; + init_sql_alloc(&m_alloc, thd()->variables.range_alloc_block_size, 0); + } + + virtual void TearDown() + { + initializer.TearDown(); + free_root(&m_alloc, MYF(0)); + } + + THD *thd() { return initializer.thd(); } + + Server_initializer initializer; + MEM_ROOT m_alloc; + RANGE_OPT_PARAM m_opt_param; +}; + +/* + Experiment with these to measure performance of + 'new (thd->mem_root)' Foo vs. 'new Foo'. + With gcc 4.4.2 I see ~4% difference (in optimized mode). +*/ +const int num_iterations= 10; +const int num_allocs= 10; + +TEST_F(SelArgTest, AllocateExplicit) +{ + for (int ix= 0; ix < num_iterations; ++ix) + { + free_root(thd()->mem_root, MYF(MY_KEEP_PREALLOC)); + for (int ii= 0; ii < num_allocs; ++ii) + new (thd()->mem_root) SEL_ARG; + } +} + +TEST_F(SelArgTest, AllocateImplicit) +{ + for (int ix= 0; ix < num_iterations; ++ix) + { + free_root(thd()->mem_root, MYF(MY_KEEP_PREALLOC)); + for (int ii= 0; ii < num_allocs; ++ii) + new SEL_ARG; + } +} + +/* + We cannot do EXPECT_NE(NULL, get_mm_tree(...)) + because of limits in google test. + */ +const SEL_TREE *null_tree= NULL; + + +class Mock_field_long : public Field_long +{ +public: + Mock_field_long() + : Field_long(0, // ptr_arg + 8, // len_arg + NULL, // null_ptr_arg + 0, // null_bit_arg + Field::NONE, // unireg_check_arg + "field_name", // field_name_arg + false, // zero_arg + false) // unsigned_arg + { + m_table_name= "mock_table"; + memset(&m_share, 0, sizeof(m_share)); + const char *foo= "mock_db"; + m_share.db.str= const_cast(foo); + m_share.db.length= strlen(m_share.db.str); + + memset(&m_table, 0, sizeof(m_table)); + m_table.s= &m_share; + this->table_name= &m_table_name; + this->table= &m_table; + } + const char *m_table_name; + TABLE_SHARE m_share; + TABLE m_table; +}; + + +TEST_F(SelArgTest, SimpleCond) +{ + EXPECT_NE(null_tree, get_mm_tree(&m_opt_param, new Item_int(42))); +} + + +TEST_F(SelArgTest, EqualCond) +{ + Mock_field_long field_long; + EXPECT_EQ(null_tree, + get_mm_tree(&m_opt_param, + new Item_equal(new Item_int(42), + new Item_field(&field_long)))); +} + +} + + === added file 'unittest/gunit/test_utils.cc' --- a/unittest/gunit/test_utils.cc 1970-01-01 00:00:00 +0000 +++ b/unittest/gunit/test_utils.cc 2011-05-13 09:36:13 +0000 @@ -0,0 +1,105 @@ +/* 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 + +#include "test_utils.h" +#include "rpl_handler.h" // delegates_init() + +namespace my_testing { + +/* + A mock error handler for error_handler_hook. +*/ +uint expected_error= 0; +extern "C" void test_error_handler_hook(uint err, const char *str, myf MyFlags) +{ + EXPECT_EQ(expected_error, err) << str; +} + +void Server_initializer::set_expected_error(uint val) +{ + expected_error= val; +} + +void Server_initializer::SetUpTestCase() +{ + static char *my_name= strdup(my_progname); + char *argv[] = { my_name, 0 }; + set_remaining_args(1, argv); + init_common_variables(); + my_init_signals(); + randominit(&sql_rand, 0, 0); + xid_cache_init(); + delegates_init(); + error_handler_hook= test_error_handler_hook; +} + +void Server_initializer::TearDownTestCase() +{ + delegates_destroy(); + xid_cache_free(); +} + +void Server_initializer::SetUp() +{ + expected_error= 0; + m_thd= new THD(false); + THD *stack_thd= m_thd; + m_thd->thread_stack= (char*) &stack_thd; + m_thd->store_globals(); + lex_start(m_thd); +} + +void Server_initializer::TearDown() +{ + m_thd->cleanup_after_query(); + delete m_thd; +} + + +Mock_error_handler::Mock_error_handler(THD *thd, uint expected_error) + : m_thd(thd), + m_expected_error(expected_error), + m_handle_called(0) +{ + thd->push_internal_handler(this); +} + +Mock_error_handler::~Mock_error_handler() +{ + // Strange Visual Studio bug: have to store 'this' in local variable. + Internal_error_handler *me= this; + EXPECT_EQ(me, m_thd->pop_internal_handler()); + EXPECT_GE(m_handle_called, 0); +} + +bool Mock_error_handler::handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + MYSQL_ERROR::enum_warning_level level, + const char* msg, + MYSQL_ERROR ** cond_hdl) +{ + EXPECT_EQ(m_expected_error, sql_errno); + ++m_handle_called; + return true; +} + + +} // namespace my_testing === added file 'unittest/gunit/test_utils.h' --- a/unittest/gunit/test_utils.h 1970-01-01 00:00:00 +0000 +++ b/unittest/gunit/test_utils.h 2011-05-13 09:36:13 +0000 @@ -0,0 +1,75 @@ +/* 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 */ + + +#ifndef TEST_UTILS_INCLUDED +#define TEST_UTILS_INCLUDED + +#include "sql_error.h" +#include "sql_class.h" + +namespace my_testing { + +/* + A class which wraps the necessary setup/teardown logic for + unit tests which depend on a working THD environment. + */ +class Server_initializer +{ +public: + Server_initializer() : m_thd(NULL) {} + + // Invoke these from corresponding functions in test fixture classes. + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); + + // Sets expected error for error_handler_hook. + static void set_expected_error(uint val); + + THD *thd() const { return m_thd; } +private: + THD *m_thd; +}; + +/** + A mock error handler which registers itself with the THD in the CTOR, + and unregisters in the DTOR. The function handle_condition() will + verify that it is called with the expected error number. + The DTOR will verify that handle_condition() has actually been called. +*/ +class Mock_error_handler : public Internal_error_handler +{ +public: + Mock_error_handler(THD *thd, uint expected_error); + virtual ~Mock_error_handler(); + + virtual bool handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + MYSQL_ERROR::enum_warning_level level, + const char* msg, + MYSQL_ERROR ** cond_hdl); +private: + THD *m_thd; + uint m_expected_error; + int m_handle_called; +}; + + +} // namespace my_testing + +#endif // TEST_UTILS_INCLUDED No bundle (reason: useless for push emails).