MySQL 8.4.0
Source Code Documentation
Component unit testing using the minimal chasiss

This framework allows testing components without loading them into the server.

It's achieved by using the "minimal chassis" library to create a test harness to test a component and a service defined by it.

The architecture is as follows:

  1. The gunit test executable defines a special gmock test suite class that sets up the test harness during its construction.
  2. the main method initializes the gmock test framework and runs it.

The test harness consists of:

  • a locally defined test harness "component" in the executable that implements mock test services that the component-under-test requires.
  • machinery to init the minimal chassis, to load the harness component and the component under test and to fetch a reference to the service under test into a test suite member variable for the tests to use.

Component testing Howto

Imagine we have a component called "foo" that requires service a bar and implements a service called foo_svc. The files for this are below:

The foo files

include/mysql/components/services/service_foo.h

DECLARE_BOOL_METHOD(mtd_foo, (int param));
#define END_SERVICE_DEFINITION(name)
A macro to end the last Service definition started with the BEGIN_SERVICE_DEFINITION macro.
Definition: service.h:91
#define BEGIN_SERVICE_DEFINITION(name)
Declares a new Service.
Definition: service.h:86
#define DECLARE_BOOL_METHOD(name, args)
Declares a method that returns bool as a part of the Service definition.
Definition: service.h:112

include/mysql/components/services/service_bar.h

DECLARE_BOOL_METHOD(mtd_bar, (int param));

components/foo/component_foo.cc

static DEFINE_BOOL_METHOD(svc_foo_mtd_foo, (int param)) {
return service_bar->mtd_bar(param);
}
BEGIN_SERVICE_IMPLEMENTATION(component_foo, service_foo)
svc_foo_mtd_foo
PROVIDES_SERVICE(component_foo, service_foo),
REQUIRES_SERVICE(service_bar),
METADATA("version", "1"),
DECLARE_COMPONENT(component_foo, "component_foo")
nullptr, nullptr
Kerberos Client Authentication nullptr
Definition: auth_kerberos_client_plugin.cc:251
Specifies macros to define Components.
#define END_COMPONENT_REQUIRES()
A macro to end the last declaration started with the BEGIN_COMPONENT_REQUIRES.
Definition: component_implementation.h:387
#define PROVIDES_SERVICE(component, service)
Declare a Service Implementation provided by a Component.
Definition: component_implementation.h:194
#define BEGIN_COMPONENT_REQUIRES(name)
A macro to specify requirements of the component.
Definition: component_implementation.h:223
#define REQUIRES_SERVICE(service)
Adds a Service requirement with a pointer to placeholder to the list of components.
Definition: component_implementation.h:305
#define REQUIRES_SERVICE_PLACEHOLDER(service)
Create a service placeholder, based on the service name.
Definition: component_implementation.h:280
#define BEGIN_COMPONENT_METADATA(name)
A macro to specify metadata of the component.
Definition: component_implementation.h:398
#define END_COMPONENT_METADATA()
A macro to end the last declaration started with the BEGIN_COMPONENT_METADATA.
Definition: component_implementation.h:413
#define DECLARE_COMPONENT(source_name, name)
Declares a component.
Definition: component_implementation.h:165
#define END_COMPONENT_PROVIDES()
A macro to end the last declaration started with the BEGIN_COMPONENT_PROVIDES.
Definition: component_implementation.h:204
#define METADATA(key, value)
Adds a Service requirement with a pointer to placeholder to the list of components.
Definition: component_implementation.h:407
#define END_DECLARE_COMPONENT()
A macro to end the last declaration of a Component.
Definition: component_implementation.h:173
#define BEGIN_COMPONENT_PROVIDES(name)
Creates a service implementation list that are provided by specified component.
Definition: component_implementation.h:183
Specifies macros to define Service Implementations.
#define BEGIN_SERVICE_IMPLEMENTATION(component, service)
Declares a Service Implementation.
Definition: service_implementation.h:62
#define END_SERVICE_IMPLEMENTATION()
A macro to end the last declaration of a Service Implementation.
Definition: service_implementation.h:67
#define DEFINE_BOOL_METHOD(name, args)
A short macro to define method that returns bool, which is the most common case.
Definition: service_implementation.h:88

Build a gunit test

Since component_foo requires service_bar we need to mock service_bar.

Thus we build a mock implementation as follows:

components/libminchassis/gunit_harness/include/mock/service_bar_empty.cc

#include "mysql/components/services/service_bar.h"
namespace service_bar_spc {
DEFINE_BOOL_METHOD(mtd_bar, (int param)) {
return false;
}
}
BEGIN_SERVICE_IMPLEMENTATION(HARNESS_COMPONENT_NAME, service_bar)
service_bar_spc::mtd_bar END_SERVICE_IMPLEMENTATION();

Now we will proceed to building a test harness "component" to embed in our test.

We will copy the template from components/libminchassis/gunit_harness/harness_component into our gunit directory components/foo/gunit and modify it as follows:

components/foo/gunit/test_harness_component.h

========== FILLME: Test Harness component name goes here ======
#define HARNESS_COMPONENT_NAME foo_harness
===============================================================
extern mysql_component_t COMPONENT_REF(HARNESS_COMPONENT_NAME);
#define COMPONENT_REF(name)
Defines a reference to the specified Component data info structure.
Definition: component_implementation.h:463
case opt name
Definition: sslopt-case.h:29
Carries information on the specific Component, all Service Implementations it provides,...
Definition: dynamic_loader.h:263

components/foo/gunit/test_harness_component.cc

#include "test_harness_component.h"
================ FILLME: Service mock includes go here ================
#include "components/libminchassis/gunit_harness/include/mock/service_bar_empty.cc"
===============================================================
#define STRINGIFY(x) #x
================ Component declaration related stuff ================
BEGIN_COMPONENT_PROVIDES(HARNESS_COMPONENT_NAME)
================ FILLME: Service mock refs go here ================
PROVIDES_SERVICE(HARNESS_COMPONENT_NAME, service_bar),
===================================================================
BEGIN_COMPONENT_REQUIRES(HARNESS_COMPONENT_NAME)
BEGIN_COMPONENT_METADATA(HARNESS_COMPONENT_NAME)
METADATA("mysql.version", "1"),
DECLARE_COMPONENT(HARNESS_COMPONENT_NAME, STRINGIFY(HARNESS_COMPONENT_NAME))
nullptr, nullptr END_DECLARE_COMPONENT();
Definition: ha_mock.cc:144
#define STRINGIFY(x)
Definition: sql_event_tracking_to_audit_event_mapping.cc:26

Now we can go ahead and implement our test driver:

components/foo/gunit/test_foo.cc

#include <gtest/gtest.h>
#include "mysql/components/services/mysql_foo.h"
#include "test_harness_component.h"
namespace foo_test {
const char component[] = "component_foo";
const char service[] = "service_foo";
using FooTest =
TestHarnessSuite_templ<SERVICE_TYPE(service_foo), component, service,
&COMPONENT_REF(HARNESS_COMPONENT_NAME)>;
TEST_F(FooTest, ComponentLoaded) {
ASSERT_NE(m_test_svc, nullptr);
ASSERT_TRUE(m_test_svc->is_valid());
}
TEST_F(FooTest, CallFoo) {
ASSERT_FALSE((*m_test_svc)->mtd_foo(12));
}
}
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
}
Implements a Gmock Test suite class to test a component.
Definition: test_harness_suite.h:55
int main(int argc, char **argv)
Definition: mysqlcheck.cc:521
#define SERVICE_TYPE(name)
Generates the standard Service type name.
Definition: service.h:76

And finally: here is the CMakeFiles.txt used for the harness component.

components/foo/gunit/CMakeLists.txt

IF (NOT WITH_UNIT_TESTS)
RETURN()
ENDIF()
MYSQL_ADD_EXECUTABLE(test_foo-t
test_foo.cc
test_harness_component.cc
LINK_LIBRARIES gtest ${CMAKE_DL_LIBS} minchassis
OpenSSL::SSL OpenSSL::Crypto
SYSTEM_INCLUDE_DIRECTORIES ${GMOCK_INCLUDE_DIRS}
ADD_TEST test_foo-t
COMPONENT Test)
@ IF
Definition: sql_yacc.h:283
See also
TestHarnessSuite_templ