unittest/gunit/components/mysql_server/dynamic_loader-t.cc (360 lines of code) (raw):
/* Copyright (c) 2016, 2017, 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-1301 USA */
#include <example_services.h>
#include <gtest/gtest.h>
#include <m_ctype.h>
#include <my_sys.h>
#include <mysql/components/component_implementation.h>
#include <mysql/components/my_service.h>
#include <mysql/components/service.h>
#include <mysql/components/service_implementation.h>
#include <mysql/components/services/dynamic_loader.h>
#include <mysql/components/services/persistent_dynamic_loader.h>
#include <mysql/mysql_lex_string.h>
#include <auth/dynamic_privileges_impl.h>
#include <udf_registration_imp.h>
#include <persistent_dynamic_loader.h>
#include <scope_guard.h>
#include <server_component.h>
#include <stddef.h>
#include "lex_string.h"
#include "my_inttypes.h"
#include "my_io.h"
extern mysql_component_t COMPONENT_REF(mysql_server);
struct mysql_component_t *mysql_builtin_components[]=
{
&COMPONENT_REF(mysql_server),
0
};
DEFINE_BOOL_METHOD(mysql_persistent_dynamic_loader_imp::load,
(void*, const char *[], int))
{
return true;
}
DEFINE_BOOL_METHOD(mysql_persistent_dynamic_loader_imp::unload,
(void*, const char *[], int))
{
return true;
}
DEFINE_BOOL_METHOD(dynamic_privilege_services_impl::register_privilege,
(const char *, size_t))
{
return true;
}
DEFINE_BOOL_METHOD(dynamic_privilege_services_impl::unregister_privilege,
(const char *, size_t))
{
return true;
}
DEFINE_BOOL_METHOD(dynamic_privilege_services_impl::has_global_grant,
(Security_context_handle, const char *, size_t))
{
return true;
}
DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_unregister,
(const char *, int *))
{
return true;
}
DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_register_aggregate,
(const char *,
enum Item_result,
Udf_func_any,
Udf_func_init,
Udf_func_deinit,
Udf_func_add,
Udf_func_clear))
{
return true;
}
DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_register,
(const char *,
Item_result,
Udf_func_any,
Udf_func_init,
Udf_func_deinit))
{
return true;
}
/* TODO following code resembles symbols used in sql library, these should be
some day extracted to be reused both in sql library and server component unit
tests. */
typedef struct charset_info_st CHARSET_INFO;
extern "C"
{
CHARSET_INFO *system_charset_info= &my_charset_latin1;
}
char opt_plugin_dir[FN_REFLEN];
bool check_string_char_length(const LEX_CSTRING &, const char *,
size_t, const CHARSET_INFO *,
bool)
{
return false;
}
bool check_valid_path(const char *path, size_t len)
{
size_t prefix= my_strcspn(system_charset_info, path, path + len, FN_DIRSEP,
strlen(FN_DIRSEP));
return prefix < len;
}
namespace dynamic_loader_unittest {
class dynamic_loader : public ::testing::Test
{
protected:
virtual void SetUp()
{
my_getwd(opt_plugin_dir, FN_REFLEN, MYF(0));
reg= NULL;
loader= NULL;
ASSERT_FALSE(mysql_services_bootstrap(®));
ASSERT_FALSE(reg->acquire("dynamic_loader", (my_h_service*)&loader));
}
virtual void TearDown()
{
if (reg)
{
ASSERT_FALSE(reg->release((my_h_service)reg));
}
if (loader)
{
ASSERT_FALSE(reg->release((my_h_service)loader));
}
shutdown_dynamic_loader();
ASSERT_FALSE(mysql_services_shutdown());
}
SERVICE_TYPE(registry)* reg;
SERVICE_TYPE(dynamic_loader)* loader;
};
TEST_F(dynamic_loader, bootstrap)
{
ASSERT_TRUE(loader != NULL);
};
TEST_F(dynamic_loader, try_load_component)
{
static const char* urns[]= { "file://component_example_component1" };
ASSERT_FALSE(loader->load(urns, 1));
ASSERT_FALSE(loader->unload(urns, 1));
}
TEST_F(dynamic_loader, try_unload_the_same_component_in_group)
{
static const char* urns[]= { "file://component_example_component1" };
ASSERT_FALSE(loader->load(urns, 1));
static const char* urns_bad[]= { "file://component_example_component1",
"file://component_example_component1" };
ASSERT_TRUE(loader->unload(urns_bad, 2));
ASSERT_FALSE(loader->unload(urns, 1));
}
TEST_F(dynamic_loader, try_load_twice)
{
static const char* urns[]= { "file://component_example_component1" };
ASSERT_FALSE(loader->load(urns, 1));
ASSERT_TRUE(loader->load(urns, 1));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_FALSE((bool)service);
}
ASSERT_FALSE(loader->unload(urns, 1));
}
TEST_F(dynamic_loader, try_load_not_existing)
{
static const char* urns[]= { "file://component_example_component0" };
ASSERT_TRUE(loader->load(urns, 1));
}
TEST_F(dynamic_loader, try_load_with_unsatisfied_dependencies)
{
static const char* urns[]= { "file://component_example_component3" };
ASSERT_TRUE(loader->load(urns, 1));
}
TEST_F(dynamic_loader, try_load_and_forget)
{
static const char* urns[]= { "file://component_example_component1" };
ASSERT_FALSE(loader->load(urns, 1));
}
TEST_F(dynamic_loader, try_unload_not_existing)
{
static const char* urns[]= { "file://component_example_component0" };
ASSERT_TRUE(loader->unload(urns, 1));
}
TEST_F(dynamic_loader, load_different_components)
{
static const char* urns1[]= { "file://component_example_component1" };
static const char* urns2[]= { "file://component_example_component2",
"file://component_example_component3" };
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
ASSERT_FALSE(loader->load(urns1, 1));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_FALSE((bool)service);
}
ASSERT_FALSE(loader->unload(urns1, 1));
ASSERT_FALSE(loader->load(urns2, 2));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_FALSE((bool)service);
}
ASSERT_FALSE(loader->unload(urns2, 2));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
}
TEST_F(dynamic_loader, dependencies)
{
static const char* urns1[]= { "file://component_example_component3" };
static const char* urns2[]= { "file://component_example_component1",
"file://component_example_component3" };
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
ASSERT_TRUE(loader->load(urns1, 1));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
ASSERT_FALSE(loader->load(urns2, 2));
ASSERT_FALSE(loader->unload(urns2, 2));
{
my_service<SERVICE_TYPE(example_math)> service("example_math", reg);
ASSERT_TRUE((bool)service);
}
}
TEST_F(dynamic_loader, cyclic_dependencies)
{
static const char* urns_self_depends[]=
{ "file://component_self_required_test_component" };
static const char* urns_cyclic_depends_broken1[]=
{ "file://component_cyclic_dependency_test_component_1" };
static const char* urns_cyclic_depends_broken2[]=
{ "file://component_cyclic_dependency_test_component_2" };
static const char* urns_cyclic_depends[]=
{ "file://component_cyclic_dependency_test_component_1",
"file://component_cyclic_dependency_test_component_2" };
/* Self-provided requirements should pass. */
ASSERT_FALSE(loader->load(urns_self_depends, 1));
ASSERT_FALSE(loader->unload(urns_self_depends, 1));
/* Broken cyclic dependency. */
ASSERT_TRUE(loader->load(urns_cyclic_depends_broken1, 1));
ASSERT_TRUE(loader->load(urns_cyclic_depends_broken2, 1));
/* Correct cyclic dependency.*/
ASSERT_FALSE(loader->load(urns_cyclic_depends, 2));
ASSERT_FALSE(loader->unload(urns_cyclic_depends, 2));
}
TEST_F(dynamic_loader, first_dependency)
{
static const char* urn1[]= { "file://component_example_component1" };
static const char* urn2[]= { "file://component_example_component2" };
static const char* urn3[]= { "file://component_example_component3" };
ASSERT_TRUE(loader->load(urn3, 1));
ASSERT_FALSE(loader->load(urn1, 1));
ASSERT_FALSE(loader->load(urn3, 1));
ASSERT_FALSE(loader->load(urn2, 1));
/*
lib2 would be sufficient for lib3 to satisfy its dependencies, but lib3 is
already using actively dependency on lib1, so we can't unload it here.
*/
ASSERT_TRUE(loader->unload(urn1, 1));
}
TEST_F(dynamic_loader, iteration)
{
my_service<SERVICE_TYPE(dynamic_loader_query)>
service("dynamic_loader_query", reg);
ASSERT_FALSE(service);
my_h_component_iterator iterator;
const char* name;
const char* urn;
int count= 0;
bool test_library_found= false;
/* No components to iterate over. */
ASSERT_TRUE(service->create(&iterator));
static const char* urns[]= { "file://component_example_component1",
"file://component_example_component2",
"file://component_example_component3" };
ASSERT_FALSE(loader->load(urns, 3));
ASSERT_FALSE(service->create(&iterator));
auto guard= create_scope_guard([&service, &iterator]()
{
service->release(iterator);
});
service->release(my_h_component_iterator{});
ASSERT_TRUE(service->get(my_h_component_iterator{}, &name, &urn));
ASSERT_TRUE(service->next(my_h_component_iterator{}));
ASSERT_TRUE(service->is_valid(my_h_component_iterator{}));
for (; !service->is_valid(iterator); service->next(iterator))
{
ASSERT_FALSE(service->get(iterator, &name, &urn));
count++;
test_library_found|= !strcmp(name, "mysql:example_component1")
&& !strcmp(urn, "file://component_example_component1");
}
ASSERT_TRUE(service->get(iterator, &name, &urn));
ASSERT_TRUE(service->next(iterator));
ASSERT_TRUE(service->is_valid(iterator));
/* there should be at least 3 test components loaded. */
ASSERT_GE(count, 3);
ASSERT_TRUE(test_library_found);
}
TEST_F(dynamic_loader, metadata)
{
my_service<SERVICE_TYPE(dynamic_loader_query)>
query_service("dynamic_loader_query", reg);
ASSERT_FALSE(query_service);
my_service<SERVICE_TYPE(dynamic_loader_metadata_enumerate)>
metadata_service("dynamic_loader_metadata_enumerate", reg);
ASSERT_FALSE(metadata_service);
my_service<SERVICE_TYPE(dynamic_loader_metadata_query)>
metadata_query_service("dynamic_loader_metadata_query", reg);
ASSERT_FALSE(metadata_query_service);
static const char* urns[]= { "file://component_example_component1",
"file://component_example_component2",
"file://component_example_component3" };
ASSERT_FALSE(loader->load(urns, 3));
my_h_component_iterator iterator;
const char* name;
const char* urn;
const char* value;
int count= 0;
bool property_found= false;
ASSERT_FALSE(query_service->create(&iterator));
auto guard= create_scope_guard([&query_service, &iterator]()
{
query_service->release(iterator);
});
for (; !query_service->is_valid(iterator); query_service->next(iterator))
{
ASSERT_FALSE(query_service->get(iterator, &name, &urn));
if (!strcmp(urn, "file://component_example_component1"))
{
ASSERT_FALSE(metadata_query_service->get_value(
iterator, "mysql.author", &value));
ASSERT_STREQ(value, "Oracle Corporation");
ASSERT_FALSE(metadata_query_service->get_value(
iterator, "mysql.license", &value));
ASSERT_STREQ(value, "GPL");
ASSERT_FALSE(metadata_query_service->get_value(
iterator, "test_property", &value));
ASSERT_TRUE(metadata_query_service->get_value(
iterator, "non_existing_test_property", &value));
my_h_component_metadata_iterator metadata_iterator;
ASSERT_FALSE(metadata_service->create(iterator, &metadata_iterator));
auto guard= create_scope_guard([&metadata_service, &metadata_iterator]()
{
metadata_service->release(metadata_iterator);
});
metadata_service->release(my_h_component_metadata_iterator{});
ASSERT_TRUE(metadata_service->get(
my_h_component_metadata_iterator{}, &name, &value));
ASSERT_TRUE(metadata_service->next(
my_h_component_metadata_iterator{}));
ASSERT_TRUE(metadata_service->is_valid(
my_h_component_metadata_iterator{}));
for (; !metadata_service->is_valid(metadata_iterator);
metadata_service->next(metadata_iterator))
{
ASSERT_FALSE(metadata_service->get(metadata_iterator, &name, &value));
count++;
property_found|= strcmp(name, "test_property");
}
ASSERT_TRUE(metadata_service->get(metadata_iterator, &name, &value));
ASSERT_TRUE(metadata_service->next(metadata_iterator));
ASSERT_TRUE(metadata_service->is_valid(metadata_iterator));
/* there should be at least 3 properties. */
ASSERT_GE(count, 3);
ASSERT_TRUE(property_found);
}
}
}
}
/* mandatory main function */
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}