Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .mci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ tasks:
make examples
${test_params} ./src/bsoncxx/test/test_bson
${test_params} ./src/mongocxx/test/test_driver
${test_params} ./src/mongocxx/test/test_instance

# The break -1 is a hack to cause us to have a bad
# exit status if any test exits with a bad exit
# status.
Expand Down
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ script:
# Run mongocxx tests with catch
- ./src/mongocxx/test/test_driver

# Run mongocxx instance tests with catch
- ./src/mongocxx/test/test_instance

# Install headers and libs for the examples
- make install

Expand Down
12 changes: 8 additions & 4 deletions src/mongocxx/collection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,10 +704,12 @@ bsoncxx::document::value collection::create_index(view_or_value keys,
return bsoncxx::builder::stream::document{} << "name" << *options.name()
<< bsoncxx::builder::stream::finalize;
} else {
return bsoncxx::builder::stream::document{}
<< "name"
<< std::string{libmongoc::collection_keys_to_index_string(bson_keys.bson())}
<< bsoncxx::builder::stream::finalize;
const auto keys = libmongoc::collection_keys_to_index_string(bson_keys.bson());

const auto clean_keys = make_guard([&] { bson_free(keys); });

return bsoncxx::builder::stream::document{} << "name" << keys
<< bsoncxx::builder::stream::finalize;
}
}

Expand All @@ -726,6 +728,8 @@ cursor collection::distinct(bsoncxx::string::view_or_value field_name, view_or_v
auto database = libmongoc::client_get_database(_get_impl().client_impl->client_t,
_get_impl().database_name.c_str());

const auto cleanup_database = make_guard([&] { libmongoc::database_destroy(database); });

auto result = libmongoc::database_command(database, MONGOC_QUERY_NONE, 0, 0, 0,
command_bson.bson(), NULL, NULL);

Expand Down
3 changes: 2 additions & 1 deletion src/mongocxx/exception/error_code.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ namespace mongocxx {
MONGOCXX_INLINE_NAMESPACE_BEGIN

enum class error_code : std::int32_t {
k_invalid_client_object = 1,
k_instance_already_exists = 1,
k_invalid_client_object,
k_invalid_collection_object,
k_invalid_database_object,
k_invalid_parameter,
Expand Down
2 changes: 2 additions & 0 deletions src/mongocxx/exception/private/error_category.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@ class mongocxx_error_category_impl final : public std::error_category {
return "invalid attempt to set an unknown read concern level";
case error_code::k_unknown_write_concern:
return "invalid attempt to set an unknown write concern level";
case error_code::k_instance_already_exists:
return "cannot create more than one mongocxx::instance object";
default:
return "unknown mongocxx error";
}
Expand Down
33 changes: 30 additions & 3 deletions src/mongocxx/instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@

#include <mongocxx/instance.hpp>

#include <mutex>
#include <utility>

#include <bsoncxx/stdx/make_unique.hpp>
#include <mongocxx/exception/logic_error.hpp>
#include <mongocxx/logger.hpp>
#include <mongocxx/exception/private/error_code.hpp>
#include <mongocxx/private/libmongoc.hpp>

#include <mongocxx/config/private/prelude.hpp>
Expand Down Expand Up @@ -57,6 +60,10 @@ void user_log_handler(::mongoc_log_level_t mongoc_log_level, const char *log_dom
stdx::string_view{log_domain}, stdx::string_view{message});
}

std::recursive_mutex instance_mutex;
instance *current_instance = nullptr;
std::unique_ptr<instance> global_instance;

} // namespace

class instance::impl {
Expand Down Expand Up @@ -85,13 +92,33 @@ class instance::impl {
instance::instance() : instance(nullptr) {
}

instance::instance(std::unique_ptr<logger> logger)
: _impl(stdx::make_unique<impl>(std::move(logger))) {
instance::instance(std::unique_ptr<logger> logger) {
std::lock_guard<std::recursive_mutex> lock(instance_mutex);
if (current_instance) {
throw logic_error(make_error_code(error_code::k_instance_already_exists));
}
_impl = stdx::make_unique<impl>(std::move(logger));
current_instance = this;
}

instance::instance(instance &&) noexcept = default;
instance &instance::operator=(instance &&) noexcept = default;
instance::~instance() = default;

instance::~instance() {
std::lock_guard<std::recursive_mutex> lock(instance_mutex);
if (current_instance != this) std::abort();
_impl.reset();
current_instance = nullptr;
}

instance &instance::current() {
std::lock_guard<std::recursive_mutex> lock(instance_mutex);
if (!current_instance) {
global_instance.reset(new instance);
current_instance = global_instance.get();
}
return *current_instance;
}

MONGOCXX_INLINE_NAMESPACE_END
} // namespace mongocxx
10 changes: 9 additions & 1 deletion src/mongocxx/instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class logger;
///
/// Class representing an instance of the MongoDB driver.
///
/// Life cycle: An instance of the driver *MUST* be kept around.
/// Life cycle: A unique instance of the driver *MUST* be kept around.
///
class MONGOCXX_API instance {
public:
Expand Down Expand Up @@ -56,6 +56,14 @@ class MONGOCXX_API instance {
///
~instance();

///
/// Returns the current unique instance of the driver. If an
/// instance was explicitly created, that will be returned. If no
/// instance has yet been created, a default instance will be
/// constructed and returned.
///
static instance& current();

private:
class MONGOCXX_PRIVATE impl;
std::unique_ptr<impl> _impl;
Expand Down
21 changes: 14 additions & 7 deletions src/mongocxx/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.

include_directories(
${THIRD_PARTY_SOURCE_DIR}/catch/include
)

link_directories(${LIBMONGOC_LIBRARY_DIRS} ${LIBBSON_LIBRARY_DIRS})

set(mongocxx_test_sources
CMakeLists.txt
bulk_write.cpp
Expand All @@ -20,7 +26,6 @@ set(mongocxx_test_sources
collection_mocked.cpp
database.cpp
hint.cpp
instance.cpp
model/update_many.cpp
options/aggregate.cpp
options/create_collection.cpp
Expand All @@ -45,21 +50,23 @@ set(mongocxx_test_sources
write_concern.cpp
)

include_directories(
${THIRD_PARTY_SOURCE_DIR}/catch/include
)

link_directories(${LIBMONGOC_LIBRARY_DIRS} ${LIBBSON_LIBRARY_DIRS})

add_executable(test_driver
${THIRD_PARTY_SOURCE_DIR}/catch/main.cpp
${mongocxx_test_sources}
)

target_link_libraries(test_driver mongocxx_mocked)

add_executable(test_instance
${THIRD_PARTY_SOURCE_DIR}/catch/main.cpp
instance.cpp
)

target_link_libraries(test_instance mongocxx_mocked)

if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(test_driver PRIVATE /bigobj)
endif()

add_test(driver test_driver)
add_test(instance test_instance)
8 changes: 7 additions & 1 deletion src/mongocxx/test/bulk_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@

#include <bsoncxx/builder/stream/document.hpp>
#include <bsoncxx/types.hpp>

#include <mongocxx/instance.hpp>
#include <mongocxx/private/libmongoc.hpp>
#include <mongocxx/bulk_write.hpp>
#include <mongocxx/write_concern.hpp>

using namespace mongocxx;

TEST_CASE("a bulk_write will setup a mongoc bulk operation", "[bulk_write]") {
instance::current();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way that catch could help us not have to do this in every single test? It seems like the only way to do that would be to change these TEST_CASEs into SECTIONs within one overarching bulk_write TEST_CASE. Probably not worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of one. It was a pain to add, but hopefully it just becomes part of how you write a test.


auto construct = libmongoc::bulk_operation_new.create_instance();
bool construct_called = false;
bool ordered_value = false;
Expand All @@ -50,6 +52,8 @@ TEST_CASE("a bulk_write will setup a mongoc bulk operation", "[bulk_write]") {
}

TEST_CASE("destruction of a bulk_write will destroy mongoc operation", "[bulk_write]") {
instance::current();

auto destruct = libmongoc::bulk_operation_destroy.create_instance();
bool destruct_called = false;

Expand Down Expand Up @@ -104,6 +108,8 @@ class FilteredDocumentFun : public SingleDocumentFun {
};

TEST_CASE("passing write operations to append calls corresponding C function", "[bulk_write]") {
instance::current();

bulk_write bw;
bsoncxx::builder::stream::document filter_builder, doc_builder, update_doc_builder;
filter_builder << "_id" << 1;
Expand Down
24 changes: 24 additions & 0 deletions src/mongocxx/test/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <mongocxx/client.hpp>
#include <mongocxx/exception/logic_error.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/private/libmongoc.hpp>
#include <mongocxx/uri.hpp>

Expand All @@ -25,24 +26,33 @@ using namespace mongocxx;
TEST_CASE("A default constructed client is false-ish", "[client]") {
MOCK_CLIENT

instance::current();

client a;
REQUIRE(!a);
}

TEST_CASE("A default constructed client cannot perform operations", "[client]") {
instance::current();

client a;
REQUIRE_THROWS_AS(a.list_databases(), mongocxx::logic_error);
}

TEST_CASE("A client constructed with a URI is truthy", "[client]") {
MOCK_CLIENT

instance::current();

client a{uri{}};
REQUIRE(a);
}

TEST_CASE("A client connects to a provided mongodb uri", "[client]") {
MOCK_CLIENT

instance::current();

std::string expected_url("mongodb://mongodb.example.com:9999");
uri mongodb_uri(expected_url);
std::string actual_url{};
Expand All @@ -62,6 +72,9 @@ TEST_CASE("A client connects to a provided mongodb uri", "[client]") {

TEST_CASE("A client cleans up its underlying mongoc client on destruction", "[client]") {
MOCK_CLIENT

instance::current();

bool destroy_called = false;
client_destroy->interpose([&](mongoc_client_t*) { destroy_called = true; });

Expand All @@ -76,6 +89,8 @@ TEST_CASE("A client cleans up its underlying mongoc client on destruction", "[cl
TEST_CASE("A client supports move operations", "[client]") {
MOCK_CLIENT

instance::current();

client a{uri{}};

bool called = false;
Expand All @@ -94,6 +109,8 @@ TEST_CASE("A client supports move operations", "[client]") {
TEST_CASE("A client has a settable Read Concern", "[collection]") {
MOCK_CLIENT

instance::current();

client mongo_client{uri{}};

auto client_set_rc_called = false;
Expand All @@ -116,6 +133,8 @@ TEST_CASE("A client has a settable Read Concern", "[collection]") {
TEST_CASE("A client's read preferences may be set and obtained", "[client]") {
MOCK_CLIENT

instance::current();

client mongo_client{uri{}};
read_preference preference{read_preference::read_mode::k_secondary_preferred};

Expand Down Expand Up @@ -143,6 +162,8 @@ TEST_CASE("A client's read preferences may be set and obtained", "[client]") {
TEST_CASE("A client's write concern may be set and obtained", "[client]") {
MOCK_CLIENT

instance::current();

client mongo_client{uri{}};
write_concern concern;
concern.majority(std::chrono::milliseconds(100));
Expand Down Expand Up @@ -181,6 +202,9 @@ TEST_CASE("A client's write concern may be set and obtained", "[client]") {

TEST_CASE("A client can create a named database object", "[client]") {
MOCK_CLIENT

instance::current();

auto database_get = libmongoc::client_get_database.create_instance();
database_get->interpose([](mongoc_client_t*, const char*) { return nullptr; }).forever();
auto database_destroy = libmongoc::database_destroy.create_instance();
Expand Down
12 changes: 11 additions & 1 deletion src/mongocxx/test/collection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
#include <bsoncxx/json.hpp>
#include <bsoncxx/stdx/string_view.hpp>
#include <bsoncxx/types.hpp>

#include <mongocxx/collection.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/exception/logic_error.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/insert_many_builder.hpp>
#include <mongocxx/pipeline.hpp>
#include <mongocxx/read_concern.hpp>
Expand All @@ -33,11 +33,15 @@ using namespace bsoncxx::builder::stream;
using namespace mongocxx;

TEST_CASE("A default constructed collection cannot perform operations", "[collection]") {
instance::current();

collection c;
REQUIRE_THROWS_AS(c.name(), mongocxx::logic_error);
}

TEST_CASE("collection copy", "[collection]") {
instance::current();

client mongodb_client{uri{}};
database db = mongodb_client["test"];

Expand All @@ -54,6 +58,8 @@ TEST_CASE("collection copy", "[collection]") {
}

TEST_CASE("collection renaming", "[collection]") {
instance::current();

client mongodb_client{uri{}};
database db = mongodb_client["test"];

Expand All @@ -70,6 +76,8 @@ TEST_CASE("collection renaming", "[collection]") {
}

TEST_CASE("CRUD functionality", "[driver::collection]") {
instance::current();

client mongodb_client{uri{}};
database db = mongodb_client["test"];
collection coll = db["mongo_cxx_driver"];
Expand Down Expand Up @@ -610,6 +618,8 @@ TEST_CASE("read_concern is inherited from parent", "[collection]") {
}

TEST_CASE("create_index returns index name", "[collection]") {
instance::current();

client mongodb_client{uri{}};
database db = mongodb_client["test"];
collection coll = db["collection"];
Expand Down
Loading