diff options
author | Andrew Morrow <acm@mongodb.com> | 2018-09-15 17:11:37 -0400 |
---|---|---|
committer | Andrew Morrow <acm@mongodb.com> | 2018-09-26 17:18:11 -0400 |
commit | 747a005fb46d3432d576fe56051b0f476e2d2c96 (patch) | |
tree | 31e10702e9a7491b02edd1c2ef043f6195372eb6 /src/mongo/embedded/mongoc_embedded | |
parent | dc66c2049ab31b927970e48b6adcc24c2724bfe7 (diff) | |
download | mongo-747a005fb46d3432d576fe56051b0f476e2d2c96.tar.gz |
SERVER-36702 Install embedded frameworks on mobile platforms
This is done by more or less maanually scripting movement and changes
to the files out of the normall $PREFIX/{include,lib} tree into
$PREFIX/Frameworks as needed to make things work. It isn't pretty, but
it gets the job done.
Later, we should define builders that know how to do these steps
directly, without needing to depend on the intermediate steps.
Diffstat (limited to 'src/mongo/embedded/mongoc_embedded')
8 files changed, 886 insertions, 0 deletions
diff --git a/src/mongo/embedded/mongoc_embedded/SConscript b/src/mongo/embedded/mongoc_embedded/SConscript new file mode 100644 index 00000000000..c4e7c612421 --- /dev/null +++ b/src/mongo/embedded/mongoc_embedded/SConscript @@ -0,0 +1,140 @@ +# -*- mode: python; -*- + +import libdeps + +Import("env") +Import("get_option") + +env = env.Clone() + +if not env['MONGO_HAVE_LIBMONGOC']: + Return() + +def create_mongoc_env(env): + mongocEnv = env.Clone() + if mongocEnv['MONGO_HAVE_LIBMONGOC'] == "framework": + mongocEnv.AppendUnique(FRAMEWORKS=['bson', 'mongoc']) + else: + mongocEnv.AppendUnique(LIBS=['bson-1.0', 'mongoc-1.0']) + return mongocEnv + +mongocEmbeddedEnv = create_mongoc_env(env) + +mongocEmbeddedEnv.AppendUnique( + CPPDEFINES=[ + 'MONGOC_EMBEDDED_COMPILING', + ], +) + +if get_option('link-model') == 'static': + mongocEmbeddedEnv.AppendUnique( + CPPDEFINES=[ + 'MONGOC_EMBEDDED_STATIC', + ], + ) + +mongocEmbeddedEnv.AppendUnique( + SHLINKFLAGS=['$MONGO_EXPORT_FILE_SHLINKFLAGS'] +) + +mongocEmbeddedEnv.Library( + target='mongoc_embedded', + source=[ + 'mongoc_embedded.cpp', + ], + LIBDEPS=[ + # No LIBDEPS or LIBDEPS_PRIVATE to mongo libraries are allowed in this library. They would get duplicated in mongo_embedded_capi. + '$BUILD_DIR/mongo/embedded/mongo_embedded/mongo_embedded', + ], + INSTALL_ALIAS=[ + 'embedded-dev', + ], +) + +if get_option('install-mode') == 'hygienic': + env.AutoInstall( + 'include/mongoc_embedded/v1/mongoc_embedded', + source=['mongoc_embedded.h'], + INSTALL_ALIAS=[ + 'embedded-dev', + ], + ) + +yamlEnv = env.Clone() +yamlEnv.InjectThirdPartyIncludePaths(libraries=['yaml']) + +if get_option('link-model') != 'dynamic-sdk': + mongocEmbeddedTestEnv = create_mongoc_env(yamlEnv) + clientTest = mongocEmbeddedTestEnv.Program( + target='mongoc_embedded_test', + source=[ + 'mongoc_embedded_test.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/server_options_core', + '$BUILD_DIR/mongo/unittest/unittest', + '$BUILD_DIR/mongo/util/options_parser/options_parser', + 'mongoc_embedded', + ], + INSTALL_ALIAS=[ + 'embedded-test', + ], + ) + + env.RegisterUnitTest(clientTest[0]); + +# Frameworkization craziness begins here. Honestly, we should do this +# better in the future in some re-usable way, but we need to get this +# thing out the door, so here goes. + +# First, we only do this in hygienic mode for the mobile targets, +# which are darwin but not macOS. For all others, we abort here. Maybe +# this should be a build flag? Since we aren't doing this for macOS, +# we can also ignore all the framework version nonsense. +if get_option('link-model') != 'dynamic-sdk' or get_option('install-mode') != 'hygienic' or not env.TargetOSIs('darwin') or env.TargetOSIs('macOS'): + Return() + +installHeaderRoot = env.Dir('$INSTALL_DIR/include/mongoc_embedded/v1/mongoc_embedded') + +frameworkDir = env.Dir('$INSTALL_DIR/Frameworks/mongoc_embedded.framework') +env.Alias('install-embedded-dev', frameworkDir) + +env.Install( + target=frameworkDir.Dir('Headers'), + source=installHeaderRoot.File('mongoc_embedded.h'), +) + +env.InstallAs( + target=frameworkDir.File('Modules/module.modulemap'), + source="mongoc_embedded.modulemap" +) + +mongocEmbeddedPlist = env.Substfile( + target="Info.plist", + source='../Info.plist.in', + SUBST_DICT=[ + ('@CFBundleExecutable@', 'mongo_embedded_mongoc_client'), + ('@CFBundleIdentifier@', 'org.mongodb.embedded_mongoc_client'), + ('@CFBundleVersion@', '$MONGO_VERSION'), + ('@CFBundleShortVersionString@', '$MONGO_VERSION') + ] +) + +env.Install( + target=frameworkDir, + source=mongocEmbeddedPlist, +) + +mongocEmbeddedFwLib = env.InstallAs( + target=frameworkDir.File('mongoc_embedded'), + source='$INSTALL_DIR/lib/libmongoc_embedded.dylib', +) + +env.AddPostAction( + files=mongocEmbeddedFwLib, + action=[ + "install_name_tool -id @rpath/mongoc_embedded.framework/mongoc_embedded $TARGET", + "install_name_tool -change @rpath/libmongo_embedded.dylib @rpath/mongo_embedded.framework/mongo_embedded $TARGET", + ], +) diff --git a/src/mongo/embedded/mongoc_embedded/libmongoc_embedded.exported_symbols_list b/src/mongo/embedded/mongoc_embedded/libmongoc_embedded.exported_symbols_list new file mode 100644 index 00000000000..0bff2c3d1fe --- /dev/null +++ b/src/mongo/embedded/mongoc_embedded/libmongoc_embedded.exported_symbols_list @@ -0,0 +1 @@ +_mongoc_embedded_v1_* diff --git a/src/mongo/embedded/mongoc_embedded/libmongoc_embedded.version_script b/src/mongo/embedded/mongoc_embedded/libmongoc_embedded.version_script new file mode 100644 index 00000000000..8d39d3b02a4 --- /dev/null +++ b/src/mongo/embedded/mongoc_embedded/libmongoc_embedded.version_script @@ -0,0 +1,4 @@ +MONGOC_EMBEDDED_ABI_1.0 { + global: mongoc_embedded_v1_*; + local: *; +}; diff --git a/src/mongo/embedded/mongoc_embedded/mongoc_embedded.cpp b/src/mongo/embedded/mongoc_embedded/mongoc_embedded.cpp new file mode 100644 index 00000000000..655c6425abe --- /dev/null +++ b/src/mongo/embedded/mongoc_embedded/mongoc_embedded.cpp @@ -0,0 +1,334 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + * + * 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/platform/basic.h" + +#include "mongoc_embedded/mongoc_embedded.h" + +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdlib> +#include <memory> +#include <stdexcept> + +#include "mongo_embedded/mongo_embedded.h" + +// Only header-only includes allowed here (except for capi.h) +#include "mongo/platform/endian.h" + +#if defined(_WIN32) +#define MONGO_API_CALL __cdecl +#else +#define MONGO_API_CALL +#endif + +// Macro to trick the linter into accepting assert. +#define mongoc_client_assert assert + +namespace { +enum RPCState { kWaitingForMessageLength, kWaitingForMessageContent, kHaveOutput }; + +// A non-owning memory view with that encapulates reading or writing from that memory by keeping +// track of a current pointer that advances on the read or write. +struct MemoryView { + MemoryView() = default; + explicit MemoryView(char* data, size_t size) + : _begin(data), _current(data), _end(data + size) {} + + char* begin() { + return _begin; + } + + char* current() { + return _current; + } + + char* end() { + return _end; + } + + // Write memory to current position and advance internal pointer + void write(const void* source, size_t size) { + if (remaining() < size) { + mongoc_client_assert(false); + return; + } + + memcpy(_current, source, size); + _current += size; + } + + // Read memory from current position and advance internal pointer + size_t read(void* destination, size_t size) { + size_t bytes_to_read = std::min(remaining(), size); + memcpy(destination, current(), bytes_to_read); + _current += bytes_to_read; + return bytes_to_read; + } + + // Size that have currently been read or written + size_t size() const { + return _current - _begin; + } + + // Total capacity for the memory this view is holding + size_t capacity() const { + return _end - _begin; + } + + // Remaining memory available for read or write + size_t remaining() const { + return _end - _current; + } + + char* _begin{nullptr}; + char* _current{nullptr}; + char* _end{nullptr}; +}; + +struct FreeDeleter { + void operator()(void* x) { + free(x); + } +}; +} // namespace + +struct mongoc_stream_embedded_t : mongoc_stream_t { + mongo_embedded_v1_client* clientHandle; + MemoryView inputBuf; + std::unique_ptr<char, FreeDeleter> hiddenBuf; + MemoryView outputBuf; + RPCState state; +}; + +namespace { + +struct FreeAndDestroy { + void operator()(mongoc_stream_t* x) { + auto stream = static_cast<mongoc_stream_embedded_t*>(x); + mongo_embedded_v1_client_destroy(stream->clientHandle, nullptr); + stream->~mongoc_stream_embedded_t(); + free(stream); + } +}; +extern "C" void mongoc_stream_embedded_destroy(mongoc_stream_t* s) try { + std::unique_ptr<mongoc_stream_t, FreeAndDestroy> stream(s); +} catch (...) { + errno = EBADMSG; +} + + +extern "C" ssize_t mongoc_stream_embedded_writev(mongoc_stream_t* s, + mongoc_iovec_t* iov, + size_t iovcnt, + int32_t timeout_msec) try { + auto stream = static_cast<mongoc_stream_embedded_t*>(s); + mongoc_client_assert(stream->state == RPCState::kWaitingForMessageContent || + stream->state == RPCState::kWaitingForMessageLength); + + u_long already_read = 0; + for (size_t i = 0; i < iovcnt; i++) { + char* current_loc = static_cast<char*>(iov[i].iov_base); + u_long remaining_iov = iov[i].iov_len; + + // do we need a new message? + if (stream->state == RPCState::kWaitingForMessageLength) { + // The message should start with a 4 byte size + int32_t message_length; + if (remaining_iov < sizeof(message_length)) { + errno = EBADMSG; + return 0; + } + + // memcpy into message_length, to be super safe in case the buffer is not 32bit aligned. + memcpy(&message_length, current_loc, sizeof(message_length)); + + // make sure we convert from network byte order to host byte order before using it. + message_length = mongo::endian::littleToNative(message_length); + + stream->hiddenBuf = std::unique_ptr<char, FreeDeleter>((char*)malloc(message_length)); + stream->inputBuf = MemoryView(stream->hiddenBuf.get(), message_length); + stream->inputBuf.write(current_loc, sizeof(message_length)); + + current_loc += sizeof(message_length); + remaining_iov -= sizeof(message_length); + already_read += sizeof(message_length); + stream->state = RPCState::kWaitingForMessageContent; + } + + // if there is no more message after reading length, we're done + if (remaining_iov <= 0) + continue; + + // copy message length into buffer + // pipelining is not allowed, so remaining_iov must be less than input_length_to_go + mongoc_client_assert(stream->inputBuf.remaining() >= remaining_iov); + stream->inputBuf.write(current_loc, remaining_iov); + + // cleanup number values to reflect the copy + already_read += remaining_iov; + remaining_iov = 0; + + // if we found a complete message, send it + if (stream->inputBuf.remaining() == 0) { + void* output_buffer; + size_t output_buffer_size; + int retVal = mongo_embedded_v1_client_invoke(stream->clientHandle, + stream->inputBuf.begin(), + stream->inputBuf.size(), + &output_buffer, + &output_buffer_size, + nullptr); + if (retVal != MONGO_EMBEDDED_V1_SUCCESS) { + return -1; + } + + // We will allocate a new one when we read in the next message length + stream->hiddenBuf.reset(); + // and then write the output to our output buffer + stream->outputBuf = MemoryView(static_cast<char*>(output_buffer), output_buffer_size); + stream->state = RPCState::kHaveOutput; + } + } + + return already_read; +} catch (...) { + errno = EBADMSG; + return 0; // not guarenteeing anything was written +} +extern "C" ssize_t mongoc_stream_embedded_readv(mongoc_stream_t* s, + mongoc_iovec_t* iov, + size_t iovcnt, + size_t min_bytes, + int32_t timeout_msec) try { + size_t bytes_read = 0; + auto stream = static_cast<mongoc_stream_embedded_t*>(s); + mongoc_client_assert(stream->state == RPCState::kHaveOutput); + for (size_t i = 0; i < iovcnt && stream->outputBuf.remaining() > 0; ++i) { + + // for each vector, fill the vector if we are able + bytes_read += stream->outputBuf.read(iov[i].iov_base, iov[i].iov_len); + } + stream->state = stream->outputBuf.remaining() == 0 ? RPCState::kWaitingForMessageLength + : RPCState::kHaveOutput; + return bytes_read; +} catch (...) { + errno = EBADMSG; + return 0; // not guarenteeing anything was read +} + + +extern "C" int mongoc_stream_embedded_close(mongoc_stream_t* s) { + return 0; +} + +extern "C" ssize_t mongoc_stream_embedded_poll(mongoc_stream_poll_t* s, + size_t array_length, + int32_t timeout_msec) try { + for (size_t i = 0; i < array_length; i++) { + s[i].revents = s[i].events & (POLLIN | POLLOUT); + } + return array_length; +} catch (...) { + errno = EBADMSG; + return -1; +} + +extern "C" bool mongoc_stream_embedded_check_closed(mongoc_stream_t* s) noexcept { + return false; +} + +extern "C" mongoc_stream_t* embedded_stream_initiator(const mongoc_uri_t* uri, + const mongoc_host_list_t* host, + void* user_data, + bson_error_t* error) try { + std::unique_ptr<unsigned char, FreeDeleter> stream_buf( + static_cast<unsigned char*>(bson_malloc0(sizeof(mongoc_stream_embedded_t)))); + if (!stream_buf) { + errno = ENOMEM; + return nullptr; + } + // Create the stream + std::unique_ptr<mongoc_stream_embedded_t, FreeAndDestroy> stream( + new (stream_buf.get()) mongoc_stream_embedded_t()); + stream_buf.release(); // This must be here so we don't have double ownership + stream->state = RPCState::kWaitingForMessageLength; + // Set up connections to database + stream->clientHandle = mongo_embedded_v1_client_create( + static_cast<mongo_embedded_v1_instance*>(user_data), nullptr); + + // Connect the functions to the stream + // type is not relevant for us. Has to be set for the C Driver, but it has to do with picking + // how to communicate over the networ + stream->type = 1000; + stream->poll = mongoc_stream_embedded_poll; + stream->close = mongoc_stream_embedded_close; + stream->readv = mongoc_stream_embedded_readv; + stream->writev = mongoc_stream_embedded_writev; + stream->destroy = mongoc_stream_embedded_destroy; + stream->check_closed = mongoc_stream_embedded_check_closed; + return stream.release(); +} catch (...) { + errno = EBADMSG; + return nullptr; +} + +struct ClientDeleter { + void operator()(mongoc_client_t* x) { + mongoc_client_destroy(x); + } +}; + +} // namespace + +extern "C" mongoc_client_t* MONGO_API_CALL +mongoc_embedded_v1_client_create(mongo_embedded_v1_instance* db) try { + if (!db) { + errno = EINVAL; + return nullptr; + } + std::unique_ptr<mongoc_client_t, ClientDeleter> client(mongoc_client_new(NULL)); + mongoc_client_set_stream_initiator(client.get(), embedded_stream_initiator, db); + return client.release(); +} catch (const std::out_of_range&) { + errno = EACCES; + return nullptr; +} catch (const std::overflow_error&) { + errno = EOVERFLOW; + return nullptr; +} catch (const std::underflow_error&) { + errno = ERANGE; + return nullptr; +} catch (const std::invalid_argument&) { + errno = EINVAL; + return nullptr; +} catch (...) { + errno = EBADMSG; + return nullptr; +} diff --git a/src/mongo/embedded/mongoc_embedded/mongoc_embedded.def b/src/mongo/embedded/mongoc_embedded/mongoc_embedded.def new file mode 100644 index 00000000000..93ad632b6a6 --- /dev/null +++ b/src/mongo/embedded/mongoc_embedded/mongoc_embedded.def @@ -0,0 +1,3 @@ +LIBRARY MONGOc_EMBEDDED +EXPORTS + mongoc_embedded_v1_client_create diff --git a/src/mongo/embedded/mongoc_embedded/mongoc_embedded.h b/src/mongo/embedded/mongoc_embedded/mongoc_embedded.h new file mode 100644 index 00000000000..4d25b316848 --- /dev/null +++ b/src/mongo/embedded/mongoc_embedded/mongoc_embedded.h @@ -0,0 +1,94 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + * + * 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. + */ +#ifndef HEADERUUID_8CAAB40D_AC65_46CF_9FA9_B48825C825DC_DEFINED +#define HEADERUUID_8CAAB40D_AC65_46CF_9FA9_B48825C825DC_DEFINED + +#include <mongo_embedded/mongo_embedded.h> +#include <mongoc/mongoc.h> + +#pragma push_macro("MONGO_API_CALL") +#undef MONGO_API_CALL + +#pragma push_macro("MONGO_API_IMPORT") +#undef MONGO_API_IMPORT + +#pragma push_macro("MONGO_API_EXPORT") +#undef MONGO_API_EXPORT + +#pragma push_macro("MONGOC_EMBEDDED_API") +#undef MONGOC_EMBEDDED_API + +#if defined(_WIN32) +#define MONGO_API_CALL __cdecl +#define MONGO_API_IMPORT __declspec(dllimport) +#define MONGO_API_EXPORT __declspec(dllexport) +#else +#define MONGO_API_CALL +#define MONGO_API_IMPORT __attribute__((visibility("default"))) +#define MONGO_API_EXPORT __attribute__((used, visibility("default"))) +#endif + +#if defined(MONGOC_EMBEDDED_STATIC) +#define MONGOC_EMBEDDED_API +#else +#if defined(MONGOC_EMBEDDED_COMPILING) +#define MONGOC_EMBEDDED_API MONGO_API_EXPORT +#else +#define MONGOC_EMBEDDED_API MONGO_API_IMPORT +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Creates a client with the correct stream intiator set + * @param db must be a valid instance handle created by `mongo_embedded_v1_instance_create` + * @returns a mongoc client or `NULL` on error + */ +MONGOC_EMBEDDED_API mongoc_client_t* MONGO_API_CALL +mongoc_embedded_v1_client_create(mongo_embedded_v1_instance* instance); + +#ifdef __cplusplus +} // extern "C" +#endif + +#undef MONGOC_EMBEDDED_API +#pragma pop_macro("MONGOC_EMBEDDED_API") + +#undef MONGO_API_EXPORT +#pragma push_macro("MONGO_API_EXPORT") + +#undef MONGO_API_IMPORT +#pragma push_macro("MONGO_API_IMPORT") + +#undef MONGO_API_CALL +#pragma pop_macro("MONGO_API_CALL") + +#endif // HEADERUUID_8CAAB40D_AC65_46CF_9FA9_B48825C825DC_DEFINED diff --git a/src/mongo/embedded/mongoc_embedded/mongoc_embedded.modulemap b/src/mongo/embedded/mongoc_embedded/mongoc_embedded.modulemap new file mode 100644 index 00000000000..9a00bdba5b4 --- /dev/null +++ b/src/mongo/embedded/mongoc_embedded/mongoc_embedded.modulemap @@ -0,0 +1,4 @@ +framework module mongoc_embedded [extern_c] { + header "mongoc_embedded.h" + export * +} diff --git a/src/mongo/embedded/mongoc_embedded/mongoc_embedded_test.cpp b/src/mongo/embedded/mongoc_embedded/mongoc_embedded_test.cpp new file mode 100644 index 00000000000..de308863b04 --- /dev/null +++ b/src/mongo/embedded/mongoc_embedded/mongoc_embedded_test.cpp @@ -0,0 +1,306 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + * + * 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. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault + +#include "mongo/platform/basic.h" + +#include "mongoc_embedded/mongoc_embedded.h" + +#include <set> + +#include <mongoc/mongoc.h> +#include <yaml-cpp/yaml.h> + +#include "mongo_embedded/mongo_embedded.h" + +#include "mongo/db/server_options.h" +#include "mongo/stdx/memory.h" +#include "mongo/unittest/temp_dir.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/log.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/signal_handlers_synchronous.h" + +namespace moe = mongo::optionenvironment; + +mongo_embedded_v1_lib* global_lib_handle; + +namespace { + +std::unique_ptr<mongo::unittest::TempDir> globalTempDir; + +/** + * WARNING: This function is an example lifted directly from the C driver + * for use testing the connection to the c driver. It is written in C, + * and should not be used for anything besides basic testing. + */ +bool insert_data(mongoc_collection_t* collection) { + mongoc_bulk_operation_t* bulk; + const int ndocs = 4; + bson_t* docs[ndocs]; + + bulk = mongoc_collection_create_bulk_operation(collection, true, NULL); + + docs[0] = BCON_NEW("x", BCON_DOUBLE(1.0), "tags", "[", "dog", "cat", "]"); + docs[1] = BCON_NEW("x", BCON_DOUBLE(2.0), "tags", "[", "cat", "]"); + docs[2] = BCON_NEW("x", BCON_DOUBLE(2.0), "tags", "[", "mouse", "cat", "dog", "]"); + docs[3] = BCON_NEW("x", BCON_DOUBLE(3.0), "tags", "[", "]"); + + for (int i = 0; i < ndocs; i++) { + mongoc_bulk_operation_insert(bulk, docs[i]); + bson_destroy(docs[i]); + docs[i] = NULL; + } + + bson_error_t error; + bool ret = mongoc_bulk_operation_execute(bulk, NULL, &error); + + if (!ret) { + ::mongo::log() << "Error inserting data: " << error.message; + } + + mongoc_bulk_operation_destroy(bulk); + return ret; +} + +/** + * WARNING: This function is an example lifted directly from the C driver + * for use testing the connection to the c driver. It is written in C, + * and should not be used for anything besides basic testing. + */ +bool explain(mongoc_collection_t* collection) { + + bson_t* command; + bson_t reply; + bson_error_t error; + bool res; + + command = BCON_NEW("explain", + "{", + "find", + BCON_UTF8((const char*)"things"), + "filter", + "{", + "x", + BCON_INT32(1), + "}", + "}"); + res = mongoc_collection_command_simple(collection, command, NULL, &reply, &error); + if (!res) { + ::mongo::log() << "Error with explain: " << error.message; + goto explain_cleanup; + } + + +explain_cleanup: + bson_destroy(&reply); + bson_destroy(command); + return res; +} + +class MongodbEmbeddedTransportLayerTest : public mongo::unittest::Test { +protected: + void setUp() { + if (!globalTempDir) { + globalTempDir = std::make_unique<mongo::unittest::TempDir>("embedded_mongo"); + } + + 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; + + db_handle = mongo_embedded_v1_instance_create(global_lib_handle, yaml.c_str(), nullptr); + + cd_client = mongoc_embedded_v1_client_create(db_handle); + mongoc_client_set_error_api(cd_client, 2); + cd_db = mongoc_client_get_database(cd_client, "test"); + cd_collection = mongoc_database_get_collection(cd_db, (const char*)"things"); + } + + void tearDown() { + mongoc_collection_drop(cd_collection, nullptr); + if (cd_collection) { + mongoc_collection_destroy(cd_collection); + } + + if (cd_db) { + mongoc_database_destroy(cd_db); + } + + if (cd_client) { + mongoc_client_destroy(cd_client); + } + + mongo_embedded_v1_instance_destroy(db_handle, nullptr); + } + + mongo_embedded_v1_instance* getDBHandle() { + return db_handle; + } + + mongoc_database_t* getDB() { + return cd_db; + } + mongoc_client_t* getClient() { + return cd_client; + } + mongoc_collection_t* getCollection() { + return cd_collection; + } + + +private: + mongo_embedded_v1_instance* db_handle; + mongoc_database_t* cd_db; + mongoc_client_t* cd_client; + mongoc_collection_t* cd_collection; +}; + +TEST_F(MongodbEmbeddedTransportLayerTest, CreateAndDestroyDB) { + // Test the setUp() and tearDown() test fixtures +} +TEST_F(MongodbEmbeddedTransportLayerTest, InsertAndExplain) { + auto client = getClient(); + auto collection = getCollection(); + ASSERT(client); + + + ASSERT(insert_data(collection)); + + ASSERT(explain(collection)); +} +TEST_F(MongodbEmbeddedTransportLayerTest, InsertAndCount) { + auto client = getClient(); + auto collection = getCollection(); + ASSERT(client); + ASSERT(collection); + bson_error_t err; + int64_t count; + ASSERT(insert_data(collection)); + count = mongoc_collection_count(collection, MONGOC_QUERY_NONE, nullptr, 0, 0, NULL, &err); + ASSERT(count == 4); +} +TEST_F(MongodbEmbeddedTransportLayerTest, InsertAndDelete) { + auto client = getClient(); + auto collection = getCollection(); + ASSERT(client); + ASSERT(collection); + bson_error_t err; + bson_oid_t oid; + int64_t count; + // done with setup + + auto doc = bson_new(); + bson_oid_init(&oid, NULL); + BSON_APPEND_OID(doc, "_id", &oid); + BSON_APPEND_UTF8(doc, "hello", "world"); + ASSERT(mongoc_collection_insert(collection, MONGOC_INSERT_NONE, doc, NULL, &err)); + count = mongoc_collection_count(collection, MONGOC_QUERY_NONE, nullptr, 0, 0, NULL, &err); + ASSERT(1 == count); + bson_destroy(doc); + doc = bson_new(); + BSON_APPEND_OID(doc, "_id", &oid); + ASSERT(mongoc_collection_remove(collection, MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &err)); + ASSERT(0 == mongoc_collection_count(collection, MONGOC_QUERY_NONE, nullptr, 0, 0, NULL, &err)); + bson_destroy(doc); +} + +struct StatusDestroy { + void operator()(mongo_embedded_v1_status* const ptr) { + if (!ptr) { + mongo_embedded_v1_status_destroy(ptr); + } + } +}; +using StatusPtr = std::unique_ptr<mongo_embedded_v1_status, StatusDestroy>; +} // 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(int argc, char** argv, char** envp) { + + moe::OptionsParser parser; + moe::Environment environment; + moe::OptionSection options; + std::map<std::string, std::string> env; + + options.addOptionChaining( + "tempPath", "tempPath", moe::String, "directory to place mongo::TempDir subdirectories"); + std::vector<std::string> argVector(argv, argv + argc); + mongo::Status ret = parser.run(options, argVector, env, &environment); + if (!ret.isOK()) { + std::cerr << options.helpString(); + return EXIT_FAILURE; + } + if (environment.count("tempPath")) { + ::mongo::unittest::TempDir::setTempPath(environment["tempPath"].as<std::string>()); + } + + ::mongo::clearSignalMask(); + ::mongo::setupSynchronousSignalHandlers(); + ::mongo::serverGlobalParams.noUnixSocket = true; + ::mongo::unittest::setupTestLogger(); + + StatusPtr status(mongo_embedded_v1_status_create()); + mongoc_init(); + + mongo_embedded_v1_init_params params; + params.log_flags = MONGO_EMBEDDED_V1_LOG_STDOUT; + params.log_callback = nullptr; + params.log_user_data = nullptr; + + global_lib_handle = mongo_embedded_v1_lib_init(¶ms, status.get()); + if (global_lib_handle == nullptr) { + std::cerr << "Error: " << mongo_embedded_v1_status_get_explanation(status.get()); + return EXIT_FAILURE; + } + + auto result = ::mongo::unittest::Suite::run(std::vector<std::string>(), "", 1); + + if (mongo_embedded_v1_lib_fini(global_lib_handle, status.get()) != MONGO_EMBEDDED_V1_SUCCESS) { + std::cerr << "Error: " << mongo_embedded_v1_status_get_explanation(status.get()); + return EXIT_FAILURE; + } + + mongoc_cleanup(); + globalTempDir.reset(); + mongo::quickExit(result); +} |