/**
* Copyright (C) 2017 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/embedded/capi.h"
#include
#include
#include
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/json.h"
#include "mongo/db/server_options.h"
#include "mongo/rpc/message.h"
#include "mongo/rpc/op_msg.h"
#include "mongo/stdx/thread.h"
#include "mongo/unittest/temp_dir.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/options_parser/environment.h"
#include "mongo/util/options_parser/option_section.h"
#include "mongo/util/options_parser/options_parser.h"
#include "mongo/util/quick_exit.h"
#include "mongo/util/shared_buffer.h"
#include "mongo/util/signal_handlers_synchronous.h"
namespace moe = mongo::optionenvironment;
mongo_embedded_v1_lib* global_lib_handle;
namespace {
std::unique_ptr globalTempDir;
struct StatusDestructor {
void operator()(mongo_embedded_v1_status* const p) const noexcept {
if (p)
mongo_embedded_v1_status_destroy(p);
}
};
using CapiStatusPtr = std::unique_ptr;
CapiStatusPtr makeStatusPtr() {
return CapiStatusPtr{mongo_embedded_v1_status_create()};
}
struct ClientDestructor {
void operator()(mongo_embedded_v1_client* const p) const noexcept {
if (!p)
return;
auto status = makeStatusPtr();
if (mongo_embedded_v1_client_destroy(p, status.get()) != MONGO_EMBEDDED_V1_SUCCESS) {
std::cerr << "libmongodb_capi_client_destroy failed." << std::endl;
if (status) {
std::cerr << "Error code: " << mongo_embedded_v1_status_get_error(status.get())
<< std::endl;
std::cerr << "Error message: "
<< mongo_embedded_v1_status_get_explanation(status.get()) << std::endl;
}
}
}
};
using MongoDBCAPIClientPtr = std::unique_ptr;
class MongodbCAPITest : public mongo::unittest::Test {
protected:
void setUp() {
status = mongo_embedded_v1_status_create();
ASSERT(status != nullptr);
if (!globalTempDir) {
globalTempDir = std::make_unique("embedded_mongo");
}
mongo_embedded_v1_init_params params;
params.log_flags = 0;
params.log_callback = nullptr;
params.log_user_data = nullptr;
YAML::Emitter yaml;
yaml << YAML::BeginMap;
yaml << YAML::Key << "storage";
yaml << YAML::Value << YAML::BeginMap;
yaml << YAML::Key << "dbPath";
yaml << YAML::Value << globalTempDir->path();
yaml << YAML::EndMap; // storage
yaml << YAML::EndMap;
params.yaml_config = yaml.c_str();
lib = mongo_embedded_v1_lib_init(¶ms, status);
ASSERT(lib != nullptr) << mongo_embedded_v1_status_get_explanation(status);
db = mongo_embedded_v1_instance_create(lib, yaml.c_str(), status);
ASSERT(db != nullptr) << mongo_embedded_v1_status_get_explanation(status);
}
void tearDown() {
ASSERT_EQUALS(mongo_embedded_v1_instance_destroy(db, status), MONGO_EMBEDDED_V1_SUCCESS)
<< mongo_embedded_v1_status_get_explanation(status);
ASSERT_EQUALS(mongo_embedded_v1_lib_fini(lib, status), MONGO_EMBEDDED_V1_SUCCESS)
<< mongo_embedded_v1_status_get_explanation(status);
mongo_embedded_v1_status_destroy(status);
}
mongo_embedded_v1_instance* getDB() const {
return db;
}
MongoDBCAPIClientPtr createClient() const {
MongoDBCAPIClientPtr client(mongo_embedded_v1_client_create(db, status));
ASSERT(client.get() != nullptr) << mongo_embedded_v1_status_get_explanation(status);
return client;
}
mongo::Message messageFromBuffer(void* data, size_t dataLen) {
auto sb = mongo::SharedBuffer::allocate(dataLen);
memcpy(sb.get(), data, dataLen);
mongo::Message msg(std::move(sb));
return msg;
}
mongo::BSONObj performRpc(MongoDBCAPIClientPtr& client, mongo::OpMsgRequest request) {
auto inputMessage = request.serialize();
// declare the output size and pointer
void* output;
size_t outputSize;
// call the wire protocol
int err = mongo_embedded_v1_client_invoke(
client.get(), inputMessage.buf(), inputMessage.size(), &output, &outputSize, status);
ASSERT_EQUALS(err, MONGO_EMBEDDED_V1_SUCCESS);
// convert the shared buffer to a mongo::message and ensure that it is valid
auto outputMessage = messageFromBuffer(output, outputSize);
ASSERT(outputMessage.size() > 0);
ASSERT(outputMessage.operation() == inputMessage.operation());
// convert the message into an OpMessage to examine its BSON
auto outputOpMsg = mongo::OpMsg::parseOwned(outputMessage);
ASSERT(outputOpMsg.body.valid(mongo::BSONVersion::kLatest));
return outputOpMsg.body;
}
protected:
mongo_embedded_v1_lib* lib;
mongo_embedded_v1_instance* db;
mongo_embedded_v1_status* status;
};
TEST_F(MongodbCAPITest, CreateAndDestroyDB) {
// Test the setUp() and tearDown() test fixtures
}
TEST_F(MongodbCAPITest, CreateAndDestroyDBAndClient) {
auto client = createClient();
}
// This test is to make sure that destroying the db will fail if there's remaining clients left.
TEST_F(MongodbCAPITest, DoNotDestroyClient) {
auto client = createClient();
ASSERT(mongo_embedded_v1_instance_destroy(getDB(), nullptr) != MONGO_EMBEDDED_V1_SUCCESS);
}
TEST_F(MongodbCAPITest, CreateMultipleClients) {
const int numClients = 10;
std::set clients;
for (int i = 0; i < numClients; i++) {
clients.insert(createClient());
}
// ensure that each client is unique by making sure that the set size equals the number of
// clients instantiated
ASSERT_EQUALS(static_cast(clients.size()), numClients);
}
TEST_F(MongodbCAPITest, IsMaster) {
// create the client object
auto client = createClient();
// craft the isMaster message
mongo::BSONObj inputObj = mongo::fromjson("{isMaster: 1}");
auto inputOpMsg = mongo::OpMsgRequest::fromDBAndBody("admin", inputObj);
auto output = performRpc(client, inputOpMsg);
ASSERT(output.getBoolField("ismaster"));
}
TEST_F(MongodbCAPITest, CreateIndex) {
// create the client object
auto client = createClient();
// craft the createIndexes message
mongo::BSONObj inputObj = mongo::fromjson(
R"raw_delimiter({
createIndexes: 'items',
indexes:
[
{
key: {
task: 1
},
name: 'task_1'
}
]
})raw_delimiter");
auto inputOpMsg = mongo::OpMsgRequest::fromDBAndBody("index_db", inputObj);
auto output = performRpc(client, inputOpMsg);
ASSERT(output.hasField("ok"));
ASSERT(output.getField("ok").numberDouble() == 1.0);
ASSERT(output.getIntField("numIndexesAfter") == output.getIntField("numIndexesBefore") + 1);
}
TEST_F(MongodbCAPITest, CreateBackgroundIndex) {
// create the client object
auto client = createClient();
// craft the createIndexes message
mongo::BSONObj inputObj = mongo::fromjson(
R"raw_delimiter({
createIndexes: 'items',
indexes:
[
{
key: {
task: 1
},
name: 'task_1',
background: true
}
]
})raw_delimiter");
auto inputOpMsg = mongo::OpMsgRequest::fromDBAndBody("background_index_db", inputObj);
auto output = performRpc(client, inputOpMsg);
ASSERT(output.hasField("ok"));
ASSERT(output.getField("ok").numberDouble() != 1.0);
}
TEST_F(MongodbCAPITest, TrimMemory) {
// create the client object
auto client = createClient();
// craft the isMaster message
mongo::BSONObj inputObj = mongo::fromjson("{trimMemory: 'aggressive'}");
auto inputOpMsg = mongo::OpMsgRequest::fromDBAndBody("admin", inputObj);
performRpc(client, inputOpMsg);
}
TEST_F(MongodbCAPITest, BatteryLevel) {
// create the client object
auto client = createClient();
// craft the isMaster message
mongo::BSONObj inputObj = mongo::fromjson("{setBatteryLevel: 'low'}");
auto inputOpMsg = mongo::OpMsgRequest::fromDBAndBody("admin", inputObj);
performRpc(client, inputOpMsg);
}
TEST_F(MongodbCAPITest, InsertDocument) {
auto client = createClient();
mongo::BSONObj insertObj = mongo::fromjson(
"{insert: 'collection_name', documents: [{firstName: 'Mongo', lastName: 'DB', age: 10}]}");
auto insertOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", insertObj);
auto outputBSON = performRpc(client, insertOpMsg);
ASSERT(outputBSON.hasField("n"));
ASSERT(outputBSON.getIntField("n") == 1);
ASSERT(outputBSON.hasField("ok"));
ASSERT(outputBSON.getField("ok").numberDouble() == 1.0);
}
TEST_F(MongodbCAPITest, InsertMultipleDocuments) {
auto client = createClient();
mongo::BSONObj insertObj = mongo::fromjson(
"{insert: 'collection_name', documents: [{firstName: 'doc1FirstName', lastName: "
"'doc1LastName', age: 30}, {firstName: 'doc2FirstName', lastName: 'doc2LastName', age: "
"20}]}");
auto insertOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", insertObj);
auto outputBSON = performRpc(client, insertOpMsg);
ASSERT(outputBSON.hasField("n"));
ASSERT(outputBSON.getIntField("n") == 2);
ASSERT(outputBSON.hasField("ok"));
ASSERT(outputBSON.getField("ok").numberDouble() == 1.0);
}
TEST_F(MongodbCAPITest, KillOp) {
auto client = createClient();
mongo::stdx::thread killOpThread([this]() {
auto client = createClient();
mongo::BSONObj currentOpObj = mongo::fromjson("{currentOp: 1}");
auto currentOpMsg = mongo::OpMsgRequest::fromDBAndBody("admin", currentOpObj);
mongo::BSONObj outputBSON;
// Wait for the sleep command to start in the main test thread.
int opid = -1;
do {
outputBSON = performRpc(client, currentOpMsg);
auto inprog = outputBSON.getObjectField("inprog");
// See if we find the sleep command among the running commands
for (const auto& elt : inprog) {
auto inprogObj = inprog.getObjectField(elt.fieldNameStringData());
std::string ns = inprogObj.getStringField("ns");
if (ns == "admin.$cmd") {
opid = inprogObj.getIntField("opid");
break;
}
}
} while (opid == -1);
// Sleep command found, kill it.
std::stringstream ss;
ss << "{'killOp': 1, 'op': " << opid << "}";
mongo::BSONObj killOpObj = mongo::fromjson(ss.str());
auto killOpMsg = mongo::OpMsgRequest::fromDBAndBody("admin", killOpObj);
outputBSON = performRpc(client, killOpMsg);
ASSERT(outputBSON.hasField("ok"));
ASSERT(outputBSON.getField("ok").numberDouble() == 1.0);
});
mongo::BSONObj sleepObj = mongo::fromjson("{'sleep': {'secs': 1000}}");
auto sleepOpMsg = mongo::OpMsgRequest::fromDBAndBody("admin", sleepObj);
auto outputBSON = performRpc(client, sleepOpMsg);
ASSERT(outputBSON.hasField("ok"));
ASSERT(outputBSON.getField("ok").numberDouble() != 1.0);
ASSERT(outputBSON.getIntField("code") == mongo::ErrorCodes::Interrupted);
killOpThread.join();
}
TEST_F(MongodbCAPITest, ReadDB) {
auto client = createClient();
mongo::BSONObj findObj = mongo::fromjson("{find: 'collection_name', limit: 2}");
auto findMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", findObj);
auto outputBSON = performRpc(client, findMsg);
ASSERT(outputBSON.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON.hasField("cursor"));
ASSERT(outputBSON.getField("cursor").embeddedObject().hasField("firstBatch"));
mongo::BSONObj arrObj =
outputBSON.getField("cursor").embeddedObject().getField("firstBatch").embeddedObject();
ASSERT(arrObj.couldBeArray());
mongo::BSONObjIterator i(arrObj);
int index = 0;
while (i.moreWithEOO()) {
mongo::BSONElement e = i.next();
if (e.eoo())
break;
index++;
}
ASSERT(index == 2);
}
TEST_F(MongodbCAPITest, InsertAndRead) {
auto client = createClient();
mongo::BSONObj insertObj = mongo::fromjson(
"{insert: 'collection_name', documents: [{firstName: 'Mongo', lastName: 'DB', age: 10}]}");
auto insertOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", insertObj);
auto outputBSON1 = performRpc(client, insertOpMsg);
ASSERT(outputBSON1.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON1.hasField("n"));
ASSERT(outputBSON1.getIntField("n") == 1);
ASSERT(outputBSON1.hasField("ok"));
ASSERT(outputBSON1.getField("ok").numberDouble() == 1.0);
mongo::BSONObj findObj = mongo::fromjson("{find: 'collection_name', limit: 1}");
auto findMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", findObj);
auto outputBSON2 = performRpc(client, findMsg);
ASSERT(outputBSON2.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON2.hasField("cursor"));
ASSERT(outputBSON2.getField("cursor").embeddedObject().hasField("firstBatch"));
mongo::BSONObj arrObj =
outputBSON2.getField("cursor").embeddedObject().getField("firstBatch").embeddedObject();
ASSERT(arrObj.couldBeArray());
mongo::BSONObjIterator i(arrObj);
int index = 0;
while (i.moreWithEOO()) {
mongo::BSONElement e = i.next();
if (e.eoo())
break;
index++;
}
ASSERT(index == 1);
}
TEST_F(MongodbCAPITest, InsertAndReadDifferentClients) {
auto client1 = createClient();
auto client2 = createClient();
mongo::BSONObj insertObj = mongo::fromjson(
"{insert: 'collection_name', documents: [{firstName: 'Mongo', lastName: 'DB', age: 10}]}");
auto insertOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", insertObj);
auto outputBSON1 = performRpc(client1, insertOpMsg);
ASSERT(outputBSON1.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON1.hasField("n"));
ASSERT(outputBSON1.getIntField("n") == 1);
ASSERT(outputBSON1.hasField("ok"));
ASSERT(outputBSON1.getField("ok").numberDouble() == 1.0);
mongo::BSONObj findObj = mongo::fromjson("{find: 'collection_name', limit: 1}");
auto findMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", findObj);
auto outputBSON2 = performRpc(client2, findMsg);
ASSERT(outputBSON2.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON2.hasField("cursor"));
ASSERT(outputBSON2.getField("cursor").embeddedObject().hasField("firstBatch"));
mongo::BSONObj arrObj =
outputBSON2.getField("cursor").embeddedObject().getField("firstBatch").embeddedObject();
ASSERT(arrObj.couldBeArray());
mongo::BSONObjIterator i(arrObj);
int index = 0;
while (i.moreWithEOO()) {
mongo::BSONElement e = i.next();
if (e.eoo())
break;
index++;
}
ASSERT(index == 1);
}
TEST_F(MongodbCAPITest, InsertAndDelete) {
auto client = createClient();
mongo::BSONObj insertObj = mongo::fromjson(
"{insert: 'collection_name', documents: [{firstName: 'toDelete', lastName: 'notImportant', "
"age: 10}]}");
auto insertOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", insertObj);
auto outputBSON1 = performRpc(client, insertOpMsg);
ASSERT(outputBSON1.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON1.hasField("n"));
ASSERT(outputBSON1.getIntField("n") == 1);
ASSERT(outputBSON1.hasField("ok"));
ASSERT(outputBSON1.getField("ok").numberDouble() == 1.0);
// Delete
mongo::BSONObj deleteObj = mongo::fromjson(
"{delete: 'collection_name', deletes: [{q: {firstName: 'toDelete', age: 10}, limit: "
"1}]}");
auto deleteOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", deleteObj);
auto outputBSON2 = performRpc(client, deleteOpMsg);
ASSERT(outputBSON2.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON2.hasField("n"));
ASSERT(outputBSON2.getIntField("n") == 1);
ASSERT(outputBSON2.hasField("ok"));
ASSERT(outputBSON2.getField("ok").numberDouble() == 1.0);
}
TEST_F(MongodbCAPITest, InsertAndUpdate) {
auto client = createClient();
mongo::BSONObj insertObj = mongo::fromjson(
"{insert: 'collection_name', documents: [{firstName: 'toUpdate', lastName: 'notImportant', "
"age: 10}]}");
auto insertOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", insertObj);
auto outputBSON1 = performRpc(client, insertOpMsg);
ASSERT(outputBSON1.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON1.hasField("n"));
ASSERT(outputBSON1.getIntField("n") == 1);
ASSERT(outputBSON1.hasField("ok"));
ASSERT(outputBSON1.getField("ok").numberDouble() == 1.0);
// Update
mongo::BSONObj updateObj = mongo::fromjson(
"{update: 'collection_name', updates: [ {q: {firstName: 'toUpdate', age: 10}, u: {'$inc': "
"{age: 5}}}]}");
auto updateOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", updateObj);
auto outputBSON2 = performRpc(client, updateOpMsg);
ASSERT(outputBSON2.valid(mongo::BSONVersion::kLatest));
ASSERT(outputBSON2.hasField("ok"));
ASSERT(outputBSON2.getField("ok").numberDouble() == 1.0);
ASSERT(outputBSON2.hasField("nModified"));
ASSERT(outputBSON2.getIntField("nModified") == 1);
}
TEST_F(MongodbCAPITest, RunListCommands) {
auto client = createClient();
std::vector whitelist = {"_hashBSONElement",
"aggregate",
"buildInfo",
"collMod",
"collStats",
"configureFailPoint",
"count",
"create",
"createIndexes",
"currentOp",
"dataSize",
"dbStats",
"delete",
"distinct",
"drop",
"dropDatabase",
"dropIndexes",
"echo",
"explain",
"find",
"findAndModify",
"geoNear",
"getLastError",
"getMore",
"getParameter",
"getPrevError",
"getShardMap",
"insert",
"isMaster",
"killCursors",
"killOp",
"listCollections",
"listCommands",
"listDatabases",
"listIndexes",
"lockInfo",
"ping",
"planCacheClear",
"planCacheClearFilters",
"planCacheListFilters",
"planCacheListPlans",
"planCacheListQueryShapes",
"planCacheSetFilter",
"reIndex",
"renameCollection",
"repairCursor",
"repairDatabase",
"resetError",
"serverStatus",
"setBatteryLevel",
"setParameter",
"sleep",
"trimMemory",
"update",
"validate"};
std::sort(whitelist.begin(), whitelist.end());
mongo::BSONObj listCommandsObj = mongo::fromjson("{ listCommands: 1 }");
auto listCommandsOpMsg = mongo::OpMsgRequest::fromDBAndBody("db_name", listCommandsObj);
auto output = performRpc(client, listCommandsOpMsg);
auto commandsBSON = output["commands"];
std::vector commands;
for (const auto& element : commandsBSON.Obj()) {
commands.push_back(element.fieldNameStringData().toString());
}
std::sort(commands.begin(), commands.end());
std::vector missing;
std::vector unsupported;
std::set_difference(whitelist.begin(),
whitelist.end(),
commands.begin(),
commands.end(),
std::back_inserter(missing));
std::set_difference(commands.begin(),
commands.end(),
whitelist.begin(),
whitelist.end(),
std::back_inserter(unsupported));
if (!missing.empty()) {
std::cout << "\nMissing commands from the embedded binary:\n";
}
for (auto&& cmd : missing) {
std::cout << cmd << "\n";
}
if (!unsupported.empty()) {
std::cout << "\nUnsupported commands in the embedded binary:\n";
}
for (auto&& cmd : unsupported) {
std::cout << cmd << "\n";
}
ASSERT(missing.empty());
ASSERT(unsupported.empty());
}
// This test is temporary to make sure that only one database can be created
// This restriction may be relaxed at a later time
TEST_F(MongodbCAPITest, CreateMultipleDBs) {
auto status = makeStatusPtr();
ASSERT(status.get());
mongo_embedded_v1_instance* db2 = mongo_embedded_v1_instance_create(lib, nullptr, status.get());
ASSERT(db2 == nullptr);
ASSERT_EQUALS(mongo_embedded_v1_status_get_error(status.get()),
MONGO_EMBEDDED_V1_ERROR_DB_MAX_OPEN);
}
} // namespace
// Define main function as an entry to these tests.
// These test functions cannot use the main() defined for unittests because they
// call runGlobalInitializers(). The embedded C API calls mongoDbMain() which
// calls runGlobalInitializers().
int main(const int argc, const char* const* const argv) {
moe::Environment environment;
moe::OptionSection options;
options.addOptionChaining(
"tempPath", "tempPath", moe::String, "directory to place mongo::TempDir subdirectories");
std::map env;
mongo::Status ret = moe::OptionsParser().run(
options, std::vector(argv, argv + argc), env, &environment);
if (!ret.isOK()) {
std::cerr << options.helpString();
return EXIT_FAILURE;
}
if (environment.count("tempPath")) {
::mongo::unittest::TempDir::setTempPath(environment["tempPath"].as());
}
::mongo::clearSignalMask();
::mongo::setupSynchronousSignalHandlers();
::mongo::serverGlobalParams.noUnixSocket = true;
::mongo::unittest::setupTestLogger();
// Allocate an error descriptor for use in non-configured tests
const auto status = makeStatusPtr();
mongo::setTestCommandsEnabled(true);
// Check so we can initialize the library without providing init params
mongo_embedded_v1_lib* lib = mongo_embedded_v1_lib_init(nullptr, status.get());
if (lib == nullptr) {
std::cerr << "mongo_embedded_v1_init() failed with "
<< mongo_embedded_v1_status_get_error(status.get()) << ": "
<< mongo_embedded_v1_status_get_explanation(status.get()) << std::endl;
return EXIT_FAILURE;
}
if (mongo_embedded_v1_lib_fini(lib, status.get()) != MONGO_EMBEDDED_V1_SUCCESS) {
std::cerr << "mongo_embedded_v1_fini() failed with "
<< mongo_embedded_v1_status_get_error(status.get()) << ": "
<< mongo_embedded_v1_status_get_explanation(status.get()) << std::endl;
return EXIT_FAILURE;
}
// Initialize the library with a log callback and test so we receive at least one callback
// during the lifetime of the test
mongo_embedded_v1_init_params params{};
bool receivedCallback = false;
params.log_flags = MONGO_EMBEDDED_V1_LOG_STDOUT | MONGO_EMBEDDED_V1_LOG_CALLBACK;
params.log_callback = [](void* user_data,
const char* message,
const char* component,
const char* context,
int severety) {
ASSERT(message);
ASSERT(component);
*reinterpret_cast(user_data) = true;
};
params.log_user_data = &receivedCallback;
lib = mongo_embedded_v1_lib_init(¶ms, nullptr);
if (lib == nullptr) {
std::cerr << "mongo_embedded_v1_init() failed with "
<< mongo_embedded_v1_status_get_error(status.get()) << ": "
<< mongo_embedded_v1_status_get_explanation(status.get()) << std::endl;
}
if (mongo_embedded_v1_lib_fini(lib, nullptr) != MONGO_EMBEDDED_V1_SUCCESS) {
std::cerr << "mongo_embedded_v1_fini() failed with "
<< mongo_embedded_v1_status_get_error(status.get()) << ": "
<< mongo_embedded_v1_status_get_explanation(status.get()) << std::endl;
}
if (!receivedCallback) {
std::cerr << "Did not get a log callback." << std::endl;
}
const auto result = ::mongo::unittest::Suite::run(std::vector(), "", 1);
globalTempDir.reset();
mongo::quickExit(result);
}