summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am241
-rw-r--r--tests/README28
-rwxr-xr-xtests/coverage.sh43
-rw-r--r--tests/func/bson/f_weird_types.c71
-rw-r--r--tests/func/bson/huge_doc.c51
-rw-r--r--tests/func/mongo/client/f_client_big_packet.c57
-rw-r--r--tests/func/mongo/sync-cursor/f_sync_cursor_iterate.c88
-rw-r--r--tests/func/mongo/sync-cursor/f_sync_cursor_tailable.c115
-rw-r--r--tests/func/mongo/sync-gridfs-chunk/f_sync_gridfs_chunk.c499
-rw-r--r--tests/func/mongo/sync-gridfs-stream/f_sync_gridfs_stream.c501
-rw-r--r--tests/func/mongo/sync-pool/f_sync_pool.c169
-rw-r--r--tests/func/mongo/sync/f_sync_auto_reauth.c58
-rw-r--r--tests/func/mongo/sync/f_sync_auto_reconnect.c61
-rw-r--r--tests/func/mongo/sync/f_sync_auto_reconnect_cache.c107
-rw-r--r--tests/func/mongo/sync/f_sync_conn_seed_add.c58
-rw-r--r--tests/func/mongo/sync/f_sync_invalid_getlasterror.c27
-rw-r--r--tests/func/mongo/sync/f_sync_max_insert_size.c69
-rw-r--r--tests/func/mongo/sync/f_sync_oidtest.c44
-rw-r--r--tests/func/mongo/sync/f_sync_safe_mode.c112
-rw-r--r--tests/func/mongo/sync/f_sync_safe_mode_cache.c131
-rw-r--r--tests/func/mongo/sync/f_sync_write_error.c52
-rw-r--r--tests/libtap/Makefile.am4
-rw-r--r--tests/libtap/tap.c298
-rw-r--r--tests/libtap/tap.h85
-rw-r--r--tests/libtap/test.c183
-rw-r--r--tests/libtap/test.h84
-rw-r--r--tests/perf/bson/p_bson_find.c43
-rwxr-xr-xtests/runall17
-rw-r--r--tests/test_cleanup.c31
-rw-r--r--tests/tools/coverage-report-entry.pl70
-rw-r--r--tests/tools/coverage-report.pl125
-rw-r--r--tests/tools/coverage-report.xsl235
-rw-r--r--tests/unit/bson/bson_append_array.c65
-rw-r--r--tests/unit/bson/bson_append_binary.c56
-rw-r--r--tests/unit/bson/bson_append_boolean.c43
-rw-r--r--tests/unit/bson/bson_append_document.c67
-rw-r--r--tests/unit/bson/bson_append_double.c41
-rw-r--r--tests/unit/bson/bson_append_int32.c40
-rw-r--r--tests/unit/bson/bson_append_int64.c41
-rw-r--r--tests/unit/bson/bson_append_js_code.c66
-rw-r--r--tests/unit/bson/bson_append_js_code_w_scope.c79
-rw-r--r--tests/unit/bson/bson_append_null.c40
-rw-r--r--tests/unit/bson/bson_append_oid.c43
-rw-r--r--tests/unit/bson/bson_append_regexp.c45
-rw-r--r--tests/unit/bson/bson_append_string.c61
-rw-r--r--tests/unit/bson/bson_append_symbol.c61
-rw-r--r--tests/unit/bson/bson_append_timestamp.c41
-rw-r--r--tests/unit/bson/bson_append_utc_datetime.c41
-rw-r--r--tests/unit/bson/bson_build.c70
-rw-r--r--tests/unit/bson/bson_build_full.c71
-rw-r--r--tests/unit/bson/bson_cursor_find.c39
-rw-r--r--tests/unit/bson/bson_cursor_find_next.c33
-rw-r--r--tests/unit/bson/bson_cursor_get_array.c44
-rw-r--r--tests/unit/bson/bson_cursor_get_binary.c60
-rw-r--r--tests/unit/bson/bson_cursor_get_boolean.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_document.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_double.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_int32.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_int64.c45
-rw-r--r--tests/unit/bson/bson_cursor_get_javascript.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_javascript_w_scope.c57
-rw-r--r--tests/unit/bson/bson_cursor_get_oid.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_regex.c52
-rw-r--r--tests/unit/bson/bson_cursor_get_string.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_symbol.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_timestamp.c43
-rw-r--r--tests/unit/bson/bson_cursor_get_utc_datetime.c43
-rw-r--r--tests/unit/bson/bson_cursor_key.c30
-rw-r--r--tests/unit/bson/bson_cursor_new.c28
-rw-r--r--tests/unit/bson/bson_cursor_next.c42
-rw-r--r--tests/unit/bson/bson_cursor_type.c30
-rw-r--r--tests/unit/bson/bson_cursor_type_as_string.c31
-rw-r--r--tests/unit/bson/bson_empty.c22
-rw-r--r--tests/unit/bson/bson_find.c34
-rw-r--r--tests/unit/bson/bson_new.c28
-rw-r--r--tests/unit/bson/bson_new_from_data.c46
-rw-r--r--tests/unit/bson/bson_reset.c27
-rw-r--r--tests/unit/bson/bson_type_as_string.c40
-rw-r--r--tests/unit/bson/bson_validate_key.c36
-rw-r--r--tests/unit/mongo/client/connect.c34
-rw-r--r--tests/unit/mongo/client/connection_get_requestid.c44
-rw-r--r--tests/unit/mongo/client/connection_set_timeout.c33
-rw-r--r--tests/unit/mongo/client/disconnect.c32
-rw-r--r--tests/unit/mongo/client/packet_recv.c56
-rw-r--r--tests/unit/mongo/client/packet_send.c75
-rw-r--r--tests/unit/mongo/sync-cursor/sync_cursor_free.c34
-rw-r--r--tests/unit/mongo/sync-cursor/sync_cursor_get_data.c51
-rw-r--r--tests/unit/mongo/sync-cursor/sync_cursor_new.c40
-rw-r--r--tests/unit/mongo/sync-cursor/sync_cursor_next.c40
-rw-r--r--tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_get_chunk.c15
-rw-r--r--tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_new.c19
-rw-r--r--tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_free.c16
-rw-r--r--tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_new_from_buffer.c71
-rw-r--r--tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_find.c38
-rw-r--r--tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_close.c41
-rw-r--r--tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_find.c36
-rw-r--r--tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_new.c43
-rw-r--r--tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_read.c44
-rw-r--r--tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_seek.c65
-rw-r--r--tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_write.c50
-rw-r--r--tests/unit/mongo/sync-gridfs/sync_gridfs_file_get_metadata.c23
-rw-r--r--tests/unit/mongo/sync-gridfs/sync_gridfs_free.c35
-rw-r--r--tests/unit/mongo/sync-gridfs/sync_gridfs_get_set_chunk_size.c33
-rw-r--r--tests/unit/mongo/sync-gridfs/sync_gridfs_list.c34
-rw-r--r--tests/unit/mongo/sync-gridfs/sync_gridfs_new.c54
-rw-r--r--tests/unit/mongo/sync-gridfs/sync_gridfs_remove.c34
-rw-r--r--tests/unit/mongo/sync-pool/sync_pool_free.c11
-rw-r--r--tests/unit/mongo/sync-pool/sync_pool_new.c19
-rw-r--r--tests/unit/mongo/sync-pool/sync_pool_pick.c11
-rw-r--r--tests/unit/mongo/sync-pool/sync_pool_return.c22
-rw-r--r--tests/unit/mongo/sync/sync_cmd_authenticate.c112
-rw-r--r--tests/unit/mongo/sync/sync_cmd_authenticate_cache.c60
-rw-r--r--tests/unit/mongo/sync/sync_cmd_count.c119
-rw-r--r--tests/unit/mongo/sync/sync_cmd_create.c78
-rw-r--r--tests/unit/mongo/sync/sync_cmd_custom.c100
-rw-r--r--tests/unit/mongo/sync/sync_cmd_delete.c135
-rw-r--r--tests/unit/mongo/sync/sync_cmd_drop.c93
-rw-r--r--tests/unit/mongo/sync/sync_cmd_exists.c85
-rw-r--r--tests/unit/mongo/sync/sync_cmd_get_last_error.c35
-rw-r--r--tests/unit/mongo/sync/sync_cmd_get_last_error_full.c35
-rw-r--r--tests/unit/mongo/sync/sync_cmd_get_more.c135
-rw-r--r--tests/unit/mongo/sync/sync_cmd_index_create.c62
-rw-r--r--tests/unit/mongo/sync/sync_cmd_index_drop.c51
-rw-r--r--tests/unit/mongo/sync/sync_cmd_index_drop_all.c49
-rw-r--r--tests/unit/mongo/sync/sync_cmd_insert.c78
-rw-r--r--tests/unit/mongo/sync/sync_cmd_insert_n.c100
-rw-r--r--tests/unit/mongo/sync/sync_cmd_is_master.c65
-rw-r--r--tests/unit/mongo/sync/sync_cmd_kill_cursors.c123
-rw-r--r--tests/unit/mongo/sync/sync_cmd_ping.c81
-rw-r--r--tests/unit/mongo/sync/sync_cmd_query.c125
-rw-r--r--tests/unit/mongo/sync/sync_cmd_reset_error.c31
-rw-r--r--tests/unit/mongo/sync/sync_cmd_update.c97
-rw-r--r--tests/unit/mongo/sync/sync_cmd_user_add.c95
-rw-r--r--tests/unit/mongo/sync/sync_cmd_user_add_with_roles.c89
-rw-r--r--tests/unit/mongo/sync/sync_cmd_user_remove.c92
-rw-r--r--tests/unit/mongo/sync/sync_conn_seed_add.c24
-rw-r--r--tests/unit/mongo/sync/sync_conn_seed_add_cache.c31
-rw-r--r--tests/unit/mongo/sync/sync_connect.c22
-rw-r--r--tests/unit/mongo/sync/sync_connect_cache.c42
-rw-r--r--tests/unit/mongo/sync/sync_connect_from_cache_enforce_primary.c47
-rw-r--r--tests/unit/mongo/sync/sync_disconnect.c22
-rw-r--r--tests/unit/mongo/sync/sync_get_set_auto_reconnect.c39
-rw-r--r--tests/unit/mongo/sync/sync_get_set_max_insert_size.c44
-rw-r--r--tests/unit/mongo/sync/sync_get_set_safe_mode.c38
-rw-r--r--tests/unit/mongo/sync/sync_get_set_slaveok.c38
-rw-r--r--tests/unit/mongo/sync/sync_reconnect.c143
-rw-r--r--tests/unit/mongo/utils/oid_as_string.c26
-rw-r--r--tests/unit/mongo/utils/oid_init.c19
-rw-r--r--tests/unit/mongo/utils/oid_new.c49
-rw-r--r--tests/unit/mongo/utils/oid_new_with_time.c46
-rw-r--r--tests/unit/mongo/utils/parse_addr.c244
-rw-r--r--tests/unit/mongo/wire/cmd_custom.c67
-rw-r--r--tests/unit/mongo/wire/cmd_delete.c73
-rw-r--r--tests/unit/mongo/wire/cmd_get_more.c50
-rw-r--r--tests/unit/mongo/wire/cmd_insert.c83
-rw-r--r--tests/unit/mongo/wire/cmd_insert_n.c95
-rw-r--r--tests/unit/mongo/wire/cmd_kill_cursors.c58
-rw-r--r--tests/unit/mongo/wire/cmd_query.c117
-rw-r--r--tests/unit/mongo/wire/cmd_update.c97
-rw-r--r--tests/unit/mongo/wire/packet_get_set_data.c65
-rw-r--r--tests/unit/mongo/wire/packet_get_set_header.c58
-rw-r--r--tests/unit/mongo/wire/packet_get_set_header_raw.c56
-rw-r--r--tests/unit/mongo/wire/packet_new.c20
-rw-r--r--tests/unit/mongo/wire/reply_packet_get_data.c52
-rw-r--r--tests/unit/mongo/wire/reply_packet_get_header.c54
-rw-r--r--tests/unit/mongo/wire/reply_packet_get_nth_document.c68
166 files changed, 11047 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..b6328e0
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,241 @@
+SUBDIRS = libtap
+
+bson_unit_tests = \
+ unit/bson/bson_new \
+ unit/bson/bson_empty \
+ unit/bson/bson_validate_key \
+ \
+ unit/bson/bson_append_string \
+ unit/bson/bson_append_double \
+ unit/bson/bson_append_boolean \
+ unit/bson/bson_append_utc_datetime \
+ unit/bson/bson_append_null \
+ unit/bson/bson_append_int32 \
+ unit/bson/bson_append_int64 \
+ unit/bson/bson_append_regexp \
+ unit/bson/bson_append_binary \
+ unit/bson/bson_append_js_code \
+ unit/bson/bson_append_symbol \
+ unit/bson/bson_append_js_code_w_scope \
+ unit/bson/bson_append_timestamp \
+ unit/bson/bson_append_oid \
+ unit/bson/bson_append_document \
+ unit/bson/bson_append_array \
+ \
+ unit/bson/bson_reset \
+ unit/bson/bson_new_from_data \
+ \
+ unit/bson/bson_build \
+ unit/bson/bson_build_full \
+ \
+ unit/bson/bson_type_as_string \
+ \
+ unit/bson/bson_cursor_new \
+ unit/bson/bson_find \
+ unit/bson/bson_cursor_next \
+ unit/bson/bson_cursor_find_next \
+ unit/bson/bson_cursor_find \
+ unit/bson/bson_cursor_type \
+ unit/bson/bson_cursor_type_as_string \
+ unit/bson/bson_cursor_key \
+ \
+ unit/bson/bson_cursor_get_string \
+ unit/bson/bson_cursor_get_double \
+ unit/bson/bson_cursor_get_document \
+ unit/bson/bson_cursor_get_array \
+ unit/bson/bson_cursor_get_binary \
+ unit/bson/bson_cursor_get_oid \
+ unit/bson/bson_cursor_get_boolean \
+ unit/bson/bson_cursor_get_utc_datetime \
+ unit/bson/bson_cursor_get_regex \
+ unit/bson/bson_cursor_get_javascript \
+ unit/bson/bson_cursor_get_symbol \
+ unit/bson/bson_cursor_get_javascript_w_scope \
+ unit/bson/bson_cursor_get_int32 \
+ unit/bson/bson_cursor_get_timestamp \
+ unit/bson/bson_cursor_get_int64
+
+bson_func_tests = \
+ func/bson/huge_doc \
+ func/bson/f_weird_types
+
+bson_perf_tests = \
+ perf/bson/p_bson_find
+
+mongo_utils_unit_tests = \
+ unit/mongo/utils/oid_init \
+ unit/mongo/utils/oid_new \
+ unit/mongo/utils/oid_new_with_time \
+ unit/mongo/utils/oid_as_string \
+ unit/mongo/utils/parse_addr
+
+mongo_wire_unit_tests = \
+ unit/mongo/wire/packet_new \
+ unit/mongo/wire/packet_get_set_header \
+ unit/mongo/wire/packet_get_set_header_raw \
+ unit/mongo/wire/packet_get_set_data \
+ \
+ unit/mongo/wire/reply_packet_get_header \
+ unit/mongo/wire/reply_packet_get_data \
+ unit/mongo/wire/reply_packet_get_nth_document \
+ \
+ unit/mongo/wire/cmd_update \
+ unit/mongo/wire/cmd_insert \
+ unit/mongo/wire/cmd_insert_n \
+ unit/mongo/wire/cmd_query \
+ unit/mongo/wire/cmd_get_more \
+ unit/mongo/wire/cmd_delete \
+ unit/mongo/wire/cmd_kill_cursors \
+ unit/mongo/wire/cmd_custom
+
+mongo_client_unit_tests = \
+ unit/mongo/client/connect \
+ unit/mongo/client/disconnect \
+ unit/mongo/client/packet_send \
+ unit/mongo/client/packet_recv \
+ unit/mongo/client/connection_set_timeout \
+ unit/mongo/client/connection_get_requestid
+
+mongo_client_func_tests = \
+ func/mongo/client/f_client_big_packet
+
+mongo_sync_unit_tests = \
+ unit/mongo/sync/sync_connect \
+ unit/mongo/sync/sync_connect_cache \
+ unit/mongo/sync/sync_conn_seed_add \
+ unit/mongo/sync/sync_conn_seed_add_cache \
+ unit/mongo/sync/sync_reconnect \
+ unit/mongo/sync/sync_disconnect \
+ unit/mongo/sync/sync_get_set_auto_reconnect \
+ unit/mongo/sync/sync_get_set_safe_mode \
+ unit/mongo/sync/sync_get_set_slaveok \
+ unit/mongo/sync/sync_get_set_max_insert_size \
+ unit/mongo/sync/sync_cmd_update \
+ unit/mongo/sync/sync_cmd_insert \
+ unit/mongo/sync/sync_cmd_insert_n \
+ unit/mongo/sync/sync_cmd_query \
+ unit/mongo/sync/sync_cmd_get_more \
+ unit/mongo/sync/sync_cmd_delete \
+ unit/mongo/sync/sync_cmd_kill_cursors \
+ unit/mongo/sync/sync_cmd_custom \
+ unit/mongo/sync/sync_cmd_count \
+ unit/mongo/sync/sync_cmd_create \
+ unit/mongo/sync/sync_cmd_exists \
+ unit/mongo/sync/sync_cmd_drop \
+ unit/mongo/sync/sync_cmd_get_last_error \
+ unit/mongo/sync/sync_cmd_get_last_error_full \
+ unit/mongo/sync/sync_cmd_reset_error \
+ unit/mongo/sync/sync_cmd_is_master \
+ unit/mongo/sync/sync_cmd_ping \
+ unit/mongo/sync/sync_cmd_user_add \
+ unit/mongo/sync/sync_cmd_user_add_with_roles \
+ unit/mongo/sync/sync_cmd_user_remove \
+ unit/mongo/sync/sync_cmd_authenticate \
+ unit/mongo/sync/sync_cmd_authenticate_cache \
+ unit/mongo/sync/sync_cmd_index_create \
+ unit/mongo/sync/sync_cmd_index_drop \
+ unit/mongo/sync/sync_cmd_index_drop_all \
+ unit/mongo/sync/sync_connect_from_cache_enforce_primary
+
+mongo_sync_func_tests = \
+ func/mongo/sync/f_sync_max_insert_size \
+ func/mongo/sync/f_sync_conn_seed_add \
+ func/mongo/sync/f_sync_safe_mode \
+ func/mongo/sync/f_sync_safe_mode_cache \
+ func/mongo/sync/f_sync_auto_reconnect \
+ func/mongo/sync/f_sync_auto_reconnect_cache \
+ func/mongo/sync/f_sync_oidtest \
+ func/mongo/sync/f_sync_auto_reauth \
+ func/mongo/sync/f_sync_invalid_getlasterror \
+ func/mongo/sync/f_sync_write_error
+
+mongo_sync_cursor_unit_tests = \
+ unit/mongo/sync-cursor/sync_cursor_new \
+ unit/mongo/sync-cursor/sync_cursor_next \
+ unit/mongo/sync-cursor/sync_cursor_get_data \
+ unit/mongo/sync-cursor/sync_cursor_free
+
+mongo_sync_cursor_func_tests = \
+ func/mongo/sync-cursor/f_sync_cursor_iterate \
+ func/mongo/sync-cursor/f_sync_cursor_tailable
+
+mongo_sync_pool_unit_tests = \
+ unit/mongo/sync-pool/sync_pool_new \
+ unit/mongo/sync-pool/sync_pool_free \
+ unit/mongo/sync-pool/sync_pool_pick \
+ unit/mongo/sync-pool/sync_pool_return
+
+mongo_sync_pool_func_tests = \
+ func/mongo/sync-pool/f_sync_pool
+
+mongo_sync_gridfs_unit_tests = \
+ unit/mongo/sync-gridfs/sync_gridfs_new \
+ unit/mongo/sync-gridfs/sync_gridfs_free \
+ unit/mongo/sync-gridfs/sync_gridfs_get_set_chunk_size \
+ unit/mongo/sync-gridfs/sync_gridfs_list \
+ unit/mongo/sync-gridfs/sync_gridfs_remove \
+ unit/mongo/sync-gridfs/sync_gridfs_file_get_metadata
+
+mongo_sync_gridfs_chunk_unit_tests = \
+ unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_find \
+ unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_new_from_buffer \
+ unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_free \
+ unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_new \
+ unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_get_chunk
+
+mongo_sync_gridfs_chunk_func_tests = \
+ func/mongo/sync-gridfs-chunk/f_sync_gridfs_chunk
+
+mongo_sync_gridfs_stream_unit_tests = \
+ unit/mongo/sync-gridfs-stream/sync_gridfs_stream_find \
+ unit/mongo/sync-gridfs-stream/sync_gridfs_stream_new \
+ unit/mongo/sync-gridfs-stream/sync_gridfs_stream_read \
+ unit/mongo/sync-gridfs-stream/sync_gridfs_stream_write \
+ unit/mongo/sync-gridfs-stream/sync_gridfs_stream_seek \
+ unit/mongo/sync-gridfs-stream/sync_gridfs_stream_close
+
+mongo_sync_gridfs_stream_func_tests = \
+ func/mongo/sync-gridfs-stream/f_sync_gridfs_stream
+
+UNIT_TESTS = ${bson_unit_tests} ${mongo_utils_unit_tests} \
+ ${mongo_wire_unit_tests} ${mongo_client_unit_tests} \
+ ${mongo_sync_unit_tests} ${mongo_sync_cursor_unit_tests} \
+ ${mongo_sync_pool_unit_tests} ${mongo_sync_gridfs_unit_tests} \
+ ${mongo_sync_gridfs_chunk_unit_tests} \
+ ${mongo_sync_gridfs_stream_unit_tests}
+FUNC_TESTS = ${bson_func_tests} ${mongo_sync_func_tests} \
+ ${mongo_client_func_tests} \
+ ${mongo_sync_cursor_func_tests} ${mongo_sync_pool_func_tests} \
+ ${mongo_sync_gridfs_func_tests} \
+ ${mongo_sync_gridfs_chunk_func_tests} \
+ ${mongo_sync_gridfs_stream_func_tests}
+PERF_TESTS = ${bson_perf_tests}
+TESTCASES = ${UNIT_TESTS} ${FUNC_TESTS} ${PERF_TESTS}
+
+check_PROGRAMS = ${TESTCASES} test_cleanup
+
+AM_CFLAGS = -I$(top_srcdir)/src/ -I${top_srcdir}/tests/libtap/ @GLIB_CFLAGS@
+AM_LDFLAGS = -no-install
+LDADD = $(top_builddir)/src/libmongo-client.la ${top_builddir}/tests/libtap/libtap.la @GLIB_LIBS@
+
+EXTRA_DIST = README \
+ runall \
+ coverage.sh \
+ tools/coverage-report-entry.pl tools/coverage-report.pl \
+ tools/coverage-report.xsl
+
+PROVE = prove -e "${PROVE_ENV}" ${PROVE_OPTIONS}
+
+check-%: BASE=$(subst -,_,$(subst check-,,$@))
+check-%: TESTCASES=$(value $(BASE)_unit_tests) $(value $(BASE)_func_tests) $(value $(BASE)_tests)
+check-%: check-recursive test_cleanup ${TESTCASES}
+ $(AM_V_at) ${builddir}/test_cleanup
+ $(AM_V_GEN) srcdir=${srcdir} ${PROVE} ${TESTCASES}
+ $(AM_V_at) ${builddir}/test_cleanup
+
+check: check-recursive test_cleanup ${TESTCASES}
+ $(AM_V_at) ${builddir}/test_cleanup
+ $(AM_V_GEN) srcdir=${srcdir} ${PROVE} ${TESTCASES}
+ $(AM_V_at) ${builddir}/test_cleanup
+
+.PHONY: check
diff --git a/tests/README b/tests/README
new file mode 100644
index 0000000..f8a2c08
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,28 @@
+About the test suite -*- org -*-
+====================
+
+The test suite has two parts: the basic tests, which do not require
+anything outside of this library, and networked tests, which require a
+certain network setup if one wants to run them all.
+
+The basic tests are run as part of `make check', while to run the
+network tests, one must do a few other things, after which the
+networked tests will be run aswell:
+
+* Set up a mongodb server, and set up variables for the test suite
+
+One must set the `TEST_PRIMARY' variable to the "IP:PORT" of the
+mongodb server.
+
+For example, assuming a bourne shell:
+
+ $ TEST_PRIMARY="127.0.0.1:27017"; export TEST_PRIMARY
+
+* To test replica sets, point the test suite to a secondary node
+
+First of all, one will need to set up a Replica Set (see the mongodb
+documentation for examples and a tutorial), and point the test suite
+to a *secondary* node by setting the `TEST_SECONDARY' environment
+variable:
+
+ $ TEST_SECONDARY="127.0.0.1:27018"; export TEST_SECONDARY
diff --git a/tests/coverage.sh b/tests/coverage.sh
new file mode 100755
index 0000000..f3a32f4
--- /dev/null
+++ b/tests/coverage.sh
@@ -0,0 +1,43 @@
+#! /bin/sh
+
+install -d coverage
+rm -f coverage/report.txt
+
+for src in ${SOURCES}; do
+ case "$src" in
+ *.c)
+ obj=`echo $src | sed 's|\.c|.o|'`
+ gc=`echo $src | sed 's|\.c|.gcno|'`
+ if test -f "${builddir}/.libs/libmongo_client_la-$obj"; then
+ objdir=${builddir}/.libs
+ else
+ objdir=${builddir}
+ fi
+ if ! test -f "${objdir}/libmongo_client_la-${gc}"; then
+ continue
+ fi
+ gcov -b -f ${srcdir}/$src -o $objdir/libmongo_client_la-$obj >coverage/$src.cov
+ ;;
+ esac
+done
+
+perl ${top_srcdir}/tests/tools/coverage-report.pl coverage/*.cov >coverage/index.xml
+xsltproc ${top_srcdir}/tests/tools/coverage-report.xsl coverage/index.xml >coverage/index.html
+
+for src in ${SOURCES}; do
+ case "$src" in
+ *.c)
+ if ! test -f "${src}.gcov"; then
+ continue
+ fi
+
+ perl ${top_srcdir}/tests/tools/coverage-report-entry.pl ${src}.gcov > coverage/${src}.gcov.html
+ grep -A4 -m 1 "File '${srcdir}/$src'" coverage/$src.cov | grep -v "^--" >>coverage/report.txt
+ echo >>coverage/report.txt
+ ;;
+ esac
+done
+
+coverage=`(echo "scale=2"; echo -n "("; echo -n $(grep "Lines executed" coverage/report.txt | cut -d: -f2 | cut -d "%" -f 1) | sed -e "s, , + ,g"; echo ") / " $(grep -c "Lines executed" coverage/report.txt)) | bc -q`
+lines=`(echo -n "("; echo -n $(grep "Lines executed" coverage/report.txt | cut -d% -f2- | cut -d " " -f3-) | sed -e "s, , + ,g"; echo ")") | bc -q`
+echo "Overall coverage: $coverage% of $lines source lines" >>coverage/report.txt
diff --git a/tests/func/bson/f_weird_types.c b/tests/func/bson/f_weird_types.c
new file mode 100644
index 0000000..100db8c
--- /dev/null
+++ b/tests/func/bson/f_weird_types.c
@@ -0,0 +1,71 @@
+#include "bson.h"
+#include "tap.h"
+#include "test.h"
+
+#include "libmongo-private.h"
+
+#include <string.h>
+
+static void
+test_func_weird_types (void)
+{
+ bson *b;
+ bson_cursor *c;
+ guint8 type = BSON_TYPE_DBPOINTER;
+ gint32 slen;
+
+ b = bson_new ();
+ bson_append_int32 (b, "int32", 42);
+
+ /* Append weird stuff */
+ b->data = g_byte_array_append (b->data, (const guint8 *)&type, sizeof (type));
+ b->data = g_byte_array_append (b->data, (const guint8 *)"dbpointer",
+ strlen ("dbpointer") + 1);
+ slen = GINT32_TO_LE (strlen ("refname") + 1);
+ b->data = g_byte_array_append (b->data, (const guint8 *)&slen, sizeof (gint32));
+ b->data = g_byte_array_append (b->data, (const guint8 *)"refname",
+ strlen ("refname") + 1);
+ b->data = g_byte_array_append (b->data, (const guint8 *)"0123456789ABCDEF",
+ 12);
+
+ bson_append_boolean (b, "Here be dragons?", TRUE);
+ bson_finish (b);
+
+ c = bson_find (b, "Here be dragons?");
+ ok (c != NULL,
+ "bson_find() can find elements past unsupported BSON types");
+ bson_cursor_free (c);
+ bson_free (b);
+
+ /* Now do it again, but append a type we can't iterate over */
+ b = bson_new ();
+ bson_append_int32 (b, "int32", 42);
+
+ /* Append BSON_TYPE_NONE */
+ type = BSON_TYPE_NONE;
+ b->data = g_byte_array_append (b->data, (const guint8 *)&type, sizeof (type));
+ b->data = g_byte_array_append (b->data, (const guint8 *)"dbpointer",
+ strlen ("dbpointer") + 1);
+ b->data = g_byte_array_append (b->data, (const guint8 *)"0123456789ABCDEF",
+ 12);
+
+ bson_append_boolean (b, "Here be dragons?", TRUE);
+ bson_finish (b);
+
+ c = bson_find (b, "Here be dragons?");
+ ok (c == NULL,
+ "bson_find() should bail out when encountering an invalid element.");
+ bson_cursor_free (c);
+
+ c = bson_cursor_new (b);
+ bson_cursor_next (c); /* This will find the first element, and
+ position us there. */
+ bson_cursor_next (c); /* This positions after the first element. */
+ ok (bson_cursor_next (c) == FALSE,
+ "bson_cursor_next() should bail out when encountering an invalid element.");
+ bson_cursor_free (c);
+
+ bson_free (b);
+}
+
+RUN_TEST (3, func_weird_types);
diff --git a/tests/func/bson/huge_doc.c b/tests/func/bson/huge_doc.c
new file mode 100644
index 0000000..d5daafe
--- /dev/null
+++ b/tests/func/bson/huge_doc.c
@@ -0,0 +1,51 @@
+#include "bson.h"
+#include "tap.h"
+#include "test.h"
+
+#ifndef HUGE_DOC_SIZE
+#define HUGE_DOC_SIZE (1024 * 1024)
+#endif
+
+#include <string.h>
+
+static void
+test_bson_huge_doc (void)
+{
+ bson *b, *s;
+ bson_cursor *c;
+ gchar *buffer;
+ gint32 ds1;
+
+ buffer = (gchar *)g_malloc (HUGE_DOC_SIZE);
+ memset (buffer, 'a', HUGE_DOC_SIZE);
+ buffer[HUGE_DOC_SIZE - 1] = '\0';
+
+ b = bson_new ();
+ bson_append_int32 (b, "preamble", 1);
+ bson_append_string (b, "huge", buffer, -1);
+ bson_append_int32 (b, "post", 1234);
+ bson_finish (b);
+ ds1 = bson_size (b);
+
+ g_free (buffer);
+
+ s = bson_new ();
+ bson_append_document (s, "hugedoc", b);
+ bson_finish (s);
+ bson_free (b);
+
+ cmp_ok (bson_size (s), ">", ds1,
+ "Document embedding another huge one, has bigger size");
+
+ c = bson_find (s, "hugedoc");
+ bson_cursor_get_document (c, &b);
+
+ cmp_ok (bson_size (b), "==", ds1,
+ "The embedded document has the correct, huge size");
+
+ bson_cursor_free (c);
+ bson_free (s);
+ bson_free (b);
+}
+
+RUN_TEST (2, bson_huge_doc);
diff --git a/tests/func/mongo/client/f_client_big_packet.c b/tests/func/mongo/client/f_client_big_packet.c
new file mode 100644
index 0000000..38176ff
--- /dev/null
+++ b/tests/func/mongo/client/f_client_big_packet.c
@@ -0,0 +1,57 @@
+#include "test.h"
+#include "mongo.h"
+
+#define BIG_PACKET_SIZE 2 * 1024 * 1024
+
+void
+test_func_client_big_packet (void)
+{
+ mongo_connection *conn;
+ mongo_packet *p;
+
+ guint8 *data;
+ bson *b;
+ gint32 exp_size;
+
+ conn = mongo_connect (config.primary_host, config.primary_port);
+
+ b = bson_new_sized (BIG_PACKET_SIZE + 1024);
+ data = g_malloc (BIG_PACKET_SIZE);
+ memset (data, 'z', BIG_PACKET_SIZE);
+ bson_append_boolean (b, "big_packet_size", TRUE);
+ bson_append_binary (b, "bighead", BSON_BINARY_SUBTYPE_GENERIC,
+ data, BIG_PACKET_SIZE);
+ bson_finish (b);
+ exp_size = bson_size (b);
+
+ p = mongo_wire_cmd_insert (1, config.ns, b, NULL);
+ mongo_packet_send (conn, p);
+ bson_free (b);
+ mongo_wire_packet_free (p);
+
+ b = bson_new ();
+ bson_append_boolean (b, "big_packet_size", TRUE);
+ bson_finish (b);
+
+ p = mongo_wire_cmd_query (2, config.ns, 0, 0, 1, b, NULL);
+ mongo_packet_send (conn, p);
+ mongo_wire_packet_free (p);
+ bson_free (b);
+
+ p = mongo_packet_recv (conn);
+ ok (p != NULL,
+ "mongo_packet_recv() works with a huge packet");
+
+ mongo_wire_reply_packet_get_nth_document (p, 1, &b);
+ bson_finish (b);
+ mongo_wire_packet_free (p);
+
+ cmp_ok (exp_size + 17, "==", bson_size (b), /* +17: _id + value */
+ "Huge packet receiving works, and returns a same sized packet");
+
+ bson_free (b);
+
+ mongo_disconnect (conn);
+}
+
+RUN_NET_TEST (2, func_client_big_packet);
diff --git a/tests/func/mongo/sync-cursor/f_sync_cursor_iterate.c b/tests/func/mongo/sync-cursor/f_sync_cursor_iterate.c
new file mode 100644
index 0000000..56ccb77
--- /dev/null
+++ b/tests/func/mongo/sync-cursor/f_sync_cursor_iterate.c
@@ -0,0 +1,88 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+#include <string.h>
+
+void
+test_func_mongo_sync_cursor_iterate (void)
+{
+ mongo_sync_connection *conn;
+ bson *query, *result;
+ mongo_sync_cursor *sc;
+ bson_cursor *c;
+ gint i;
+ gint32 first_i32 = -1, last_i32 = -1, current_i32 = -1;
+ gboolean early_break = FALSE, continous = TRUE;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+
+ for (i = 0; i < 10; i++)
+ {
+ bson *data = bson_new ();
+ bson_append_boolean (data, "f_sync_cursor_iterate", TRUE);
+ bson_append_int32 (data, "i32", 42 * 100 + i);
+ bson_finish (data);
+
+ mongo_sync_cmd_insert (conn, config.ns, data, NULL);
+ bson_free (data);
+ }
+
+ query = bson_new ();
+ bson_append_boolean (query, "f_sync_cursor_iterate", TRUE);
+ bson_finish (query);
+
+ sc = mongo_sync_cursor_new (conn, config.ns,
+ mongo_sync_cmd_query (conn, config.ns, 0, 0, 3,
+ query, NULL));
+ bson_free (query);
+
+ ok (sc != NULL,
+ "mongo_sync_cursor_new() works");
+
+ result = mongo_sync_cursor_get_data (sc);
+ ok (result == NULL,
+ "mongo_sync_cursor_get_data() should fail without _cursor_next()");
+
+ i = 0;
+ while (mongo_sync_cursor_next (sc) && i < 10)
+ {
+ result = mongo_sync_cursor_get_data (sc);
+
+ if (!result)
+ {
+ early_break = TRUE;
+ break;
+ }
+ i++;
+ c = bson_find (result, "i32");
+ bson_cursor_get_int32 (c, &current_i32);
+ bson_cursor_free (c);
+ bson_free (result);
+
+ if (first_i32 == -1)
+ {
+ first_i32 = current_i32;
+ last_i32 = first_i32 - 1;
+ }
+
+ if (current_i32 != last_i32 + 1)
+ continous = FALSE;
+ last_i32 = current_i32;
+ }
+
+ ok (early_break == FALSE,
+ "mongo_sync_cursor_next() can iterate over the whole stuff");
+ ok (continous == TRUE,
+ "mongo_sync_cursor_next() iterates over all elements");
+
+ cmp_ok (first_i32, "!=", last_i32,
+ "Iteration returns different elements, as expected");
+ cmp_ok (i, ">=", 10,
+ "Iteration really does return all documents");
+
+ mongo_sync_cursor_free (sc);
+ mongo_sync_disconnect (conn);
+}
+
+RUN_NET_TEST (6, func_mongo_sync_cursor_iterate);
diff --git a/tests/func/mongo/sync-cursor/f_sync_cursor_tailable.c b/tests/func/mongo/sync-cursor/f_sync_cursor_tailable.c
new file mode 100644
index 0000000..c200ed8
--- /dev/null
+++ b/tests/func/mongo/sync-cursor/f_sync_cursor_tailable.c
@@ -0,0 +1,115 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+#include <string.h>
+
+void
+test_func_mongo_sync_cursor_tailable (void)
+{
+ mongo_sync_connection *conn;
+ bson *query, *data;
+ mongo_sync_cursor *sc, *tc;
+ mongo_packet *p;
+ gint i;
+ gchar *capped_ns, *capped_coll;
+
+ bson_cursor *c;
+ gboolean tailed = FALSE;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+
+ query = bson_new ();
+ bson_finish (query);
+
+ p = mongo_sync_cmd_query (conn, config.ns,
+ MONGO_WIRE_FLAG_QUERY_TAILABLE_CURSOR |
+ MONGO_WIRE_FLAG_QUERY_NO_CURSOR_TIMEOUT,
+ 0, 3, query, NULL);
+ ok (p == NULL,
+ "Tailable cursors should not work on non-capped collections");
+
+ capped_coll = g_strconcat (config.coll, ".capped", NULL);
+ capped_ns = g_strconcat (config.ns, ".capped", NULL);
+
+ query = bson_build (BSON_TYPE_STRING, "create", capped_coll, -1,
+ BSON_TYPE_BOOLEAN, "capped", TRUE,
+ BSON_TYPE_INT32, "size", 64 * 1024 * 10,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ mongo_sync_cmd_drop (conn, config.db, capped_coll);
+ p = mongo_sync_cmd_custom (conn, config.db, query);
+ bson_free (query);
+
+ ok (p != NULL,
+ "Creating a capped collection works");
+ mongo_wire_packet_free (p);
+
+ for (i = 0; i < 10; i++)
+ {
+ data = bson_new ();
+ bson_append_boolean (data, "f_sync_cursor_tailable", TRUE);
+ bson_append_int32 (data, "i32", 42 * 1000 + i);
+ bson_finish (data);
+
+ mongo_sync_cmd_insert (conn, capped_ns, data, NULL);
+ bson_free (data);
+ }
+
+ query = bson_new ();
+ bson_append_boolean (query, "f_sync_cursor_tailable", TRUE);
+ bson_finish (query);
+
+ tc = mongo_sync_cursor_new (conn, capped_ns,
+ mongo_sync_cmd_query (conn, capped_ns,
+ MONGO_WIRE_FLAG_QUERY_TAILABLE_CURSOR |
+ MONGO_WIRE_FLAG_QUERY_NO_CURSOR_TIMEOUT,
+ 0, 3, query, NULL));
+
+ sc = mongo_sync_cursor_new (conn, capped_ns,
+ mongo_sync_cmd_query (conn, capped_ns,
+ 0,
+ 0, 3, query, NULL));
+
+ bson_free (query);
+
+ /* Exhaust both queries */
+ for (i = 0; i < 10; i++)
+ {
+ mongo_sync_cursor_next (tc);
+ mongo_sync_cursor_next (sc);
+ }
+
+ data = bson_new ();
+ bson_append_boolean (data, "f_sync_cursor_tailable", TRUE);
+ bson_append_boolean (data, "tailed", TRUE);
+ bson_finish (data);
+
+ mongo_sync_cmd_insert (conn, capped_ns, data, NULL);
+ bson_free (data);
+
+ ok (mongo_sync_cursor_next (tc) == TRUE,
+ "mongo_sync_cursor_next() works after a tailable cursor got new data");
+ ok (mongo_sync_cursor_next (sc) == FALSE,
+ "mongo_sync_cursor_next() fails on a non-tailable cursor");
+
+ data = mongo_sync_cursor_get_data (tc);
+ ok (data != NULL,
+ "mongo_sync_cursor_get_data() works on a tailable cursor");
+ c = bson_find (data, "tailed");
+ bson_cursor_get_boolean (c, &tailed);
+ ok (tailed == TRUE,
+ "We got the appropriate data back!");
+ bson_cursor_free (c);
+
+ mongo_sync_cursor_free (sc);
+ mongo_sync_cursor_free (tc);
+
+ mongo_sync_cmd_drop (conn, config.db, capped_coll);
+ g_free (capped_ns);
+ g_free (capped_coll);
+ mongo_sync_disconnect (conn);
+}
+
+RUN_NET_TEST (6, func_mongo_sync_cursor_tailable);
diff --git a/tests/func/mongo/sync-gridfs-chunk/f_sync_gridfs_chunk.c b/tests/func/mongo/sync-gridfs-chunk/f_sync_gridfs_chunk.c
new file mode 100644
index 0000000..cac6e28
--- /dev/null
+++ b/tests/func/mongo/sync-gridfs-chunk/f_sync_gridfs_chunk.c
@@ -0,0 +1,499 @@
+#include "test.h"
+#include "mongo.h"
+
+#define FILE_SIZE 1024 * 1024 + 12345
+
+static guint8 noname_oid[12];
+static guint8 named_oid[12];
+static guint8 binsub_oid[12];
+
+void
+test_func_sync_gridfs_put (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_chunked_file *gfile;
+ bson *meta;
+ guint8 *data, *oid;
+ gchar *oid_s;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ oid = mongo_util_oid_new (1);
+ meta = bson_build (BSON_TYPE_STRING, "filename", "libmongo-test", -1,
+ BSON_TYPE_OID, "_id", oid,
+ BSON_TYPE_NONE);
+ g_free (oid);
+ bson_finish (meta);
+
+ data = g_malloc (FILE_SIZE);
+ memset (data, 'x', FILE_SIZE);
+
+ gfile = mongo_sync_gridfs_chunked_file_new_from_buffer (gfs, meta,
+ data, FILE_SIZE);
+ ok (gfile != NULL,
+ "GridFS file upload (with metadata) works!");
+ memcpy (named_oid, mongo_sync_gridfs_file_get_id (gfile), 12);
+ oid_s = mongo_util_oid_as_string (named_oid);
+ note ("Named file ID : %s\n", oid_s);
+ g_free (oid_s);
+ mongo_sync_gridfs_chunked_file_free (gfile);
+
+ gfile = mongo_sync_gridfs_chunked_file_new_from_buffer (gfs, NULL,
+ data, FILE_SIZE);
+ ok (gfile != NULL,
+ "GridFS file upload (w/o metadata) works!");
+ memcpy (noname_oid, mongo_sync_gridfs_file_get_id (gfile), 12);
+ oid_s = mongo_util_oid_as_string (noname_oid);
+ note ("Noname file ID: %s\n", oid_s);
+ g_free (oid_s);
+ mongo_sync_gridfs_chunked_file_free (gfile);
+
+ g_free (data);
+ bson_free (meta);
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_put_invalid (void)
+{
+ mongo_sync_connection *conn;
+ bson *meta;
+ gchar *ns;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ ns = g_strconcat (config.gfs_prefix, ".files", NULL);
+
+ /* Insert metadata without any of the required fields but ID. */
+ meta = bson_build (BSON_TYPE_STRING, "my-id", "id-only", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert metadata with an ID that's not an ObjectID. */
+ meta = bson_build (BSON_TYPE_STRING, "_id", "I'm a teapot", -1,
+ BSON_TYPE_STRING, "my-id", "string-id", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert metadata with invalid length type. */
+ meta = bson_build (BSON_TYPE_DOUBLE, "length", 1.0,
+ BSON_TYPE_STRING, "my-id", "invalid-length", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert metadata with invalid chunkSize type. */
+ meta = bson_build (BSON_TYPE_INT32, "length", 10,
+ BSON_TYPE_DOUBLE, "chunkSize", 12.5,
+ BSON_TYPE_STRING, "my-id", "invalid-chunkSize", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert metadata with invalid uploadDate type. */
+ meta = bson_build (BSON_TYPE_INT32, "length", 10,
+ BSON_TYPE_INT32, "chunkSize", 12,
+ BSON_TYPE_STRING, "my-id", "invalid-date", -1,
+ BSON_TYPE_INT32, "uploadDate", 1234,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert metadata with invalid md5 type. */
+ meta = bson_build (BSON_TYPE_INT32, "length", 32,
+ BSON_TYPE_INT32, "chunkSize", 12,
+ BSON_TYPE_UTC_DATETIME, "uploadDate", (gint64)1234,
+ BSON_TYPE_INT32, "md5", 0,
+ BSON_TYPE_STRING, "my-id", "invalid-md5", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert a valid metadata, without chunks. */
+ meta = bson_build (BSON_TYPE_INT32, "length", 32,
+ BSON_TYPE_INT32, "chunkSize", 12,
+ BSON_TYPE_UTC_DATETIME, "uploadDate", (gint64)1234,
+ BSON_TYPE_STRING, "md5", "deadbeef", -1,
+ BSON_TYPE_STRING, "my-id", "no-chunks", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ g_free (ns);
+ mongo_sync_disconnect (conn);
+}
+
+void
+validate_file (mongo_sync_gridfs *gfs, const bson *query, guint8 *oid,
+ gboolean validate_md5)
+{
+ mongo_sync_gridfs_chunked_file *f;
+ mongo_sync_cursor *cursor;
+ gint64 n = 0, tsize = 0;
+ const bson *meta;
+ gchar *oid_s;
+
+ f = mongo_sync_gridfs_chunked_find (gfs, query);
+
+ ok (f != NULL,
+ "File not found");
+ ok (memcmp (mongo_sync_gridfs_file_get_id (f), oid, 12) == 0,
+ "File _id matches");
+ cmp_ok (mongo_sync_gridfs_file_get_length (f), "==", FILE_SIZE,
+ "File length matches");
+ cmp_ok (mongo_sync_gridfs_file_get_chunk_size (f), "==",
+ mongo_sync_gridfs_get_chunk_size (gfs),
+ "File chunk size matches");
+
+ oid_s = mongo_util_oid_as_string (mongo_sync_gridfs_file_get_id (f));
+ note ("File info:\n\tid = %s; length = %" G_GINT64_FORMAT "; "
+ "chunk_size = %d; date = %" G_GINT64_FORMAT "; "
+ "md5 = %s; n = %" G_GINT64_FORMAT "\n",
+
+ oid_s,
+ mongo_sync_gridfs_file_get_length (f),
+ mongo_sync_gridfs_file_get_chunk_size (f),
+ mongo_sync_gridfs_file_get_date (f),
+ mongo_sync_gridfs_file_get_md5 (f),
+ mongo_sync_gridfs_file_get_chunks (f));
+ g_free (oid_s);
+ meta = mongo_sync_gridfs_file_get_metadata (f);
+ ok (meta != NULL,
+ "mongo_sync_gridfs_file_get_metadata() works");
+
+ cursor = mongo_sync_gridfs_chunked_file_cursor_new (f, 0, 0);
+ while (mongo_sync_cursor_next (cursor))
+ {
+ gint32 size;
+ guint8 *data;
+
+ data = mongo_sync_gridfs_chunked_file_cursor_get_chunk (cursor, &size);
+ g_free (data);
+
+ tsize += size;
+ n++;
+ }
+ mongo_sync_cursor_free (cursor);
+
+ if (validate_md5)
+ cmp_ok (mongo_sync_gridfs_file_get_length (f), "==", tsize,
+ "File size matches the sum of its chunks");
+ cmp_ok (mongo_sync_gridfs_file_get_chunks (f), "==", n,
+ "Number of chunks matches the expected number");
+
+ mongo_sync_gridfs_chunked_file_free (f);
+}
+
+void
+test_func_sync_gridfs_get (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ bson *query;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ query = bson_build (BSON_TYPE_STRING, "filename", "libmongo-test", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+ validate_file (gfs, query, named_oid, TRUE);
+ bson_free (query);
+
+ query = bson_build (BSON_TYPE_OID, "_id", noname_oid,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+ validate_file (gfs, query, noname_oid, TRUE);
+ bson_free (query);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_get_invalid (mongo_sync_gridfs *gfs, gchar *name, gchar *msg)
+{
+ bson *query;
+
+ query = bson_build (BSON_TYPE_STRING, "my-id", name, -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+ ok (mongo_sync_gridfs_chunked_find (gfs, query) == NULL, msg);
+ bson_free (query);
+}
+
+void
+test_func_sync_gridfs_get_invalid (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_cursor *cursor;
+ bson *query;
+ gchar *ns;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ test_get_invalid (gfs, "unknown",
+ "mongo_sync_gridfs_chunked_find() should fail when no file "
+ "is found");
+ test_get_invalid (gfs, "id-only",
+ "mongo_sync_gridfs_chunked__find() should fail if the metadata "
+ "is incomplete");
+ test_get_invalid (gfs, "string-id",
+ "mongo_sync_gridfs_chunked__find() should fail if the _id is "
+ "not an ObjectID");
+ test_get_invalid (gfs, "invalid-length",
+ "mongo_sync_gridfs_chunked__find() should fail if length is "
+ "of inappropriate type");
+ test_get_invalid (gfs, "invalid-chunkSize",
+ "mongo_sync_gridfs_chunked__find() should fail if chunkSize is "
+ "of inappropriate type");
+ test_get_invalid (gfs, "invalid-date",
+ "mongo_sync_gridfs_chunked__find() should fail if uploadDate is "
+ "of inappropriate type");
+ test_get_invalid (gfs, "invalid-md5",
+ "mongo_sync_gridfs_chunked__find() should fail if md5 is of "
+ "inappropriate type");
+
+ ns = g_strconcat (config.gfs_prefix, ".files", NULL);
+ query = bson_build (BSON_TYPE_STRING, "my-id", "id-only", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ cursor = mongo_sync_cursor_new (conn, ns,
+ mongo_sync_cmd_query (conn, ns, 0, 0, 0,
+ query, NULL));
+ bson_free (query);
+ mongo_sync_cursor_next (cursor);
+ ok (mongo_sync_gridfs_chunked_file_cursor_get_chunk (cursor, NULL) == NULL,
+ "mongo_sync_gridfs_chunked_file_cursor_get_chunk() should fail with "
+ "invalid data");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_list (void)
+{
+ mongo_sync_gridfs *gfs;
+ bson *query, *data;
+ mongo_sync_cursor *cursor;
+ bson_cursor *c;
+ const gchar *str;
+ gboolean found_named = FALSE, found_noname = FALSE;
+ const guint8 *oid;
+
+ gfs = mongo_sync_gridfs_new
+ (mongo_sync_connect (config.primary_host, config.primary_port, TRUE),
+ config.gfs_prefix);
+
+ /* Test list with an invalid query */
+ query = bson_build (BSON_TYPE_STRING, "no-such-field",
+ "You're not seeing this field.", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ cursor = mongo_sync_gridfs_list (gfs, query);
+ ok (cursor == NULL,
+ "mongo_sync_gridfs_list() should fail if there query "
+ "does not match anything");
+ bson_free (query);
+
+ /* Test list with a query */
+ query = bson_build (BSON_TYPE_OID, "_id", named_oid,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ cursor = mongo_sync_gridfs_list (gfs, query);
+ ok (cursor != NULL,
+ "mongo_sync_gridfs_list() correctly finds files by query");
+
+ mongo_sync_cursor_next (cursor);
+ data = mongo_sync_cursor_get_data (cursor);
+ c = bson_find (data, "filename");
+ bson_cursor_get_string (c, &str);
+ bson_cursor_free (c);
+
+ is (str, "libmongo-test",
+ "The listed file is named correctly");
+ bson_free (data);
+ mongo_sync_cursor_free (cursor);
+
+ bson_free (query);
+
+ /* Test list without a query */
+ cursor = mongo_sync_gridfs_list (gfs, NULL);
+ while (mongo_sync_cursor_next (cursor))
+ {
+ data = mongo_sync_cursor_get_data (cursor);
+
+ c = bson_find (data, "_id");
+ bson_cursor_get_oid (c, (const guint8 **)&oid);
+ bson_cursor_free (c);
+
+ if (memcmp (oid, named_oid, 12) == 0)
+ found_named = TRUE;
+ if (memcmp (oid, noname_oid, 12) == 0)
+ found_noname = TRUE;
+
+ bson_free (data);
+ }
+ mongo_sync_cursor_free (cursor);
+
+ ok (found_named == TRUE && found_noname == TRUE,
+ "mongo_sync_gridfs_list() finds both uploaded files without a query");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_fync_sync_gridfs_remove (void)
+{
+ mongo_sync_gridfs *gfs;
+ bson *query;
+
+ gfs = mongo_sync_gridfs_new
+ (mongo_sync_connect (config.primary_host, config.primary_port, TRUE),
+ config.gfs_prefix);
+
+ /* Test with a non-matching query */
+ query = bson_build (BSON_TYPE_STRING, "no-such-field",
+ "You're not seeing this field.", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ ok (mongo_sync_gridfs_remove (gfs, query) == FALSE,
+ "mongo_sync_gridfs_remove() should fail if there's nothing to delete.");
+ bson_free (query);
+
+ /* Test with a non-string id */
+ query = bson_build (BSON_TYPE_STRING, "my-id", "string-id", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ ok (mongo_sync_gridfs_remove (gfs, query) == FALSE,
+ "mongo_sync_gridfs_remove() should fail if the file id is not "
+ "an ObjectId");
+ bson_free (query);
+
+ /* Test with a working query */
+ query = bson_build (BSON_TYPE_OID, "_id", named_oid,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ ok (mongo_sync_gridfs_remove (gfs, query) == TRUE,
+ "mongo_sync_gridfs_remove() works");
+ bson_finish (query);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_put_binary_subtype (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_chunked_file *gfile;
+ bson *meta, *query, *update;
+ guint8 *data;
+ gchar *chunk_ns;
+ guint32 size = GINT32_TO_LE(FILE_SIZE);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ meta = bson_build (BSON_TYPE_STRING, "filename", "binsub-libmongo-test", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ data = g_malloc (FILE_SIZE + 4);
+ memcpy (data, &size, 4);
+ memset (data + 4, 'x', FILE_SIZE);
+
+ gfile = mongo_sync_gridfs_chunked_file_new_from_buffer (gfs, meta,
+ data + 4, FILE_SIZE);
+ memcpy (binsub_oid, mongo_sync_gridfs_file_get_id (gfile), 12);
+
+ query = bson_build (BSON_TYPE_OID, "files_id",
+ mongo_sync_gridfs_file_get_id (gfile),
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ mongo_sync_gridfs_chunked_file_free (gfile);
+ bson_free (meta);
+
+ update = bson_build_full (BSON_TYPE_DOCUMENT, "$set", TRUE,
+ bson_build (BSON_TYPE_BINARY, "data",
+ BSON_BINARY_SUBTYPE_BINARY,
+ data, FILE_SIZE + 4,
+ BSON_TYPE_NONE),
+ BSON_TYPE_NONE);
+ bson_finish (update);
+ g_free (data);
+
+ chunk_ns = g_strconcat (config.gfs_prefix, ".chunks", NULL);
+ mongo_sync_cmd_update (conn, chunk_ns, MONGO_WIRE_FLAG_UPDATE_UPSERT,
+ query, update);
+
+ bson_free (query);
+ bson_free (update);
+ g_free (chunk_ns);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_get_binary_subtype (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ bson *query;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ query = bson_build (BSON_TYPE_STRING, "filename", "binsub-libmongo-test", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+ validate_file (gfs, query, binsub_oid, FALSE);
+ bson_free (query);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_chunk (void)
+{
+ mongo_util_oid_init (0);
+
+ test_func_sync_gridfs_put ();
+ test_func_sync_gridfs_get ();
+ test_func_sync_gridfs_list ();
+
+ sleep (2);
+
+ test_func_sync_gridfs_put_binary_subtype ();
+ test_func_sync_gridfs_get_binary_subtype ();
+
+ test_func_sync_gridfs_put_invalid ();
+ test_func_sync_gridfs_get_invalid ();
+
+ test_fync_sync_gridfs_remove ();
+}
+
+RUN_NET_TEST (37, func_sync_gridfs_chunk);
diff --git a/tests/func/mongo/sync-gridfs-stream/f_sync_gridfs_stream.c b/tests/func/mongo/sync-gridfs-stream/f_sync_gridfs_stream.c
new file mode 100644
index 0000000..a2c3690
--- /dev/null
+++ b/tests/func/mongo/sync-gridfs-stream/f_sync_gridfs_stream.c
@@ -0,0 +1,501 @@
+#include "test.h"
+#include "mongo.h"
+#include "compat.h"
+
+#define FILE_SIZE 1024 * 1024 + 12345
+#define BUFFER_SIZE 64 * 1024
+
+gchar *write_md5 = NULL;
+static gint seq = 1;
+
+void
+test_func_sync_gridfs_stream_without_oid_init (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, NULL);
+ ok (stream == NULL,
+ "mongo_sync_gridfs_stream_new() fails without mongo_util_oid_init()");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_write (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ bson *meta;
+ guint8 *data, *oid;
+ gint pos = 0;
+ gint filler = 0;
+ gboolean write_ok = TRUE;
+ GChecksum *chk;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ oid = mongo_util_oid_new (seq++);
+ meta = bson_build (BSON_TYPE_STRING, "filename", "libmongo-test-stream", -1,
+ BSON_TYPE_OID, "_id", oid,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+ g_free (oid);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, meta);
+ ok (stream != NULL,
+ "mongo_sync_gridfs_stream_new() works");
+ bson_free (meta);
+
+ data = g_malloc (BUFFER_SIZE);
+
+ chk = g_checksum_new (G_CHECKSUM_MD5);
+
+ while (pos < FILE_SIZE)
+ {
+ gint csize = BUFFER_SIZE;
+
+ if (csize + pos > FILE_SIZE)
+ csize = FILE_SIZE - pos;
+
+ memset (data, filler++, BUFFER_SIZE);
+
+ g_checksum_update (chk, data, csize);
+
+ write_ok &= mongo_sync_gridfs_stream_write (stream, data, csize);
+ pos += csize;
+ }
+ ok (write_ok == TRUE,
+ "All stream_write()s succeeded");
+
+ write_md5 = g_strdup (g_checksum_get_string (chk));
+ g_checksum_free (chk);
+
+ note ("File MD5: %s\n", write_md5);
+
+ g_free (data);
+ ok (mongo_sync_gridfs_stream_close (stream) == TRUE,
+ "mongo_sync_gridfs_stream_close() works");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_write_binary_subtype (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ bson *meta, *update;
+ guint8 *data, *oid;
+ gboolean write_ok = TRUE;
+ guint32 size = GINT32_TO_LE(BUFFER_SIZE);
+ gchar *ns;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ oid = mongo_util_oid_new (seq++);
+ meta = bson_build (BSON_TYPE_STRING, "filename", "libmongo-test-stream-bintype", -1,
+ BSON_TYPE_OID, "_id", oid,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+
+ stream = mongo_sync_gridfs_stream_new (gfs, meta);
+ ok (stream != NULL,
+ "mongo_sync_gridfs_stream_new() works");
+ bson_free (meta);
+
+ data = g_malloc (BUFFER_SIZE + 4);
+ memcpy (data, &size, 4);
+ memset (data + 4, 'x', BUFFER_SIZE);
+ write_ok = mongo_sync_gridfs_stream_write (stream, data + 4, BUFFER_SIZE);
+ ok (write_ok == TRUE,
+ "All stream_write()s succeeded");
+
+ ok (mongo_sync_gridfs_stream_close (stream) == TRUE,
+ "mongo_sync_gridfs_stream_close() works");
+
+ meta = bson_build (BSON_TYPE_OID, "files_id", oid,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ update = bson_build_full (BSON_TYPE_DOCUMENT, "$set", TRUE,
+ bson_build (BSON_TYPE_BINARY, "data",
+ BSON_BINARY_SUBTYPE_BINARY,
+ data, BUFFER_SIZE + 4,
+ BSON_TYPE_NONE),
+ BSON_TYPE_NONE);
+ bson_finish (update);
+ g_free (data);
+
+ ns = g_strconcat (config.gfs_prefix, ".chunks", NULL);
+ mongo_sync_cmd_update (conn, ns, MONGO_WIRE_FLAG_UPDATE_UPSERT,
+ meta, update);
+ bson_free (meta);
+ bson_free (update);
+ g_free (ns);
+ g_free (oid);
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_write_invalid (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ bson *meta;
+ gchar *ns;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ ns = g_strconcat (config.gfs_prefix, ".files", NULL);
+
+ /* Try to write a file with a custom, non-OID _id */
+ meta = bson_build (BSON_TYPE_STRING, "filename", "lmc-invalid-id", -1,
+ BSON_TYPE_STRING, "_id", "Short and stout", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, meta);
+ ok (stream == NULL,
+ "mongo_sync_gridfs_stream_new() should fail if meta has an invalid _id");
+ bson_free (meta);
+
+ /* Write a file with a non-OID _id, bypassing the GridFS API. */
+ meta = bson_build (BSON_TYPE_STRING, "_id", "Short and stout", -1,
+ BSON_TYPE_STRING, "my-id", "stream:string-id", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert metadata with invalid length type. */
+ meta = bson_build (BSON_TYPE_DOUBLE, "length", 1.0,
+ BSON_TYPE_STRING, "my-id", "stream:invalid-length", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert metadata with invalid chunkSize type. */
+ meta = bson_build (BSON_TYPE_INT32, "length", 10,
+ BSON_TYPE_DOUBLE, "chunkSize", 12.5,
+ BSON_TYPE_STRING, "my-id", "stream:invalid-chunkSize", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ /* Insert a valid metadata, without chunks. */
+ meta = bson_build (BSON_TYPE_INT32, "length", 32,
+ BSON_TYPE_INT32, "chunkSize", 12,
+ BSON_TYPE_UTC_DATETIME, "uploadDate", (gint64)1234,
+ BSON_TYPE_STRING, "md5", "deadbeef", -1,
+ BSON_TYPE_STRING, "my-id", "stream:no-chunks", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ mongo_sync_cmd_insert (conn, ns, meta, NULL);
+ bson_free (meta);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_read (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ guint8 data[12345];
+ gint64 pos = 0;
+ bson *meta;
+
+ GChecksum *chk;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ meta = bson_build (BSON_TYPE_STRING, "filename", "libmongo-test-stream", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ ok (stream != NULL,
+ "mongo_sync_gridfs_stream_find() works");
+ bson_free (meta);
+
+ chk = g_checksum_new (G_CHECKSUM_MD5);
+
+ while (pos < FILE_SIZE)
+ {
+ gint64 r;
+
+ r = mongo_sync_gridfs_stream_read (stream, data, sizeof (data));
+ if (r == -1)
+ break;
+
+ g_checksum_update (chk, data, r);
+ pos += r;
+ }
+
+ cmp_ok (pos, "==", FILE_SIZE,
+ "mongo_sync_gridfs_stream_read() works");
+ is (g_checksum_get_string (chk), write_md5,
+ "md5sums match");
+
+ g_checksum_free (chk);
+ ok (mongo_sync_gridfs_stream_close (stream) == TRUE,
+ "mongo_sync_gridfs_stream_close() works");
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_read_binary_subtype (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ guint8 *data;
+ gint64 r;
+ bson *meta;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ meta = bson_build (BSON_TYPE_STRING, "filename", "libmongo-test-stream-bintype", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ ok (stream != NULL,
+ "mongo_sync_gridfs_stream_find() works");
+ bson_free (meta);
+
+ data = g_malloc (BUFFER_SIZE);
+ r = mongo_sync_gridfs_stream_read (stream, data, BUFFER_SIZE);
+ cmp_ok (r, "==", BUFFER_SIZE,
+ "mongo_sync_gridfs_stream_read() works");
+
+ ok (mongo_sync_gridfs_stream_close (stream) == TRUE,
+ "mongo_sync_gridfs_stream_close() works");
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_meta (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ bson *meta;
+ const guint8 *id;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ meta = bson_build (BSON_TYPE_STRING, "filename", "libmongo-test-stream", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ bson_free (meta);
+
+ id = mongo_sync_gridfs_file_get_id (stream);
+ ok (id != NULL,
+ "mongo_sync_gridfs_file_get_id() works on streams");
+
+ ok (mongo_sync_gridfs_file_get_md5 (stream) == NULL,
+ "mongo_sync_gridfs_file_get_md5() fails on streams");
+ ok (mongo_sync_gridfs_file_get_date (stream) == -1,
+ "mongo_sync_gridfs_file_get_date() fails on streams");
+ ok (mongo_sync_gridfs_file_get_metadata (stream) == NULL,
+ "mongo_sync_gridfs_file_get_metadata() fails on streams");
+
+ mongo_sync_gridfs_stream_close (stream);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_read_invalid (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ guint8 data[1245];
+ gint64 r;
+ bson *meta;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ /* ---- */
+ meta = bson_build (BSON_TYPE_STRING, "my-id", "stream:string-id", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ ok (stream == NULL,
+ "mongo_sync_gridfs_stream_find() should fail if _id is non-OID");
+ bson_free (meta);
+
+ /* ---- */
+ meta = bson_build (BSON_TYPE_STRING, "my-id", "stream:invalid-length", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ ok (stream == NULL,
+ "mongo_sync_gridfs_stream_find() should fail with invalid metadata");
+ bson_free (meta);
+
+ /* ---- */
+ meta = bson_build (BSON_TYPE_STRING, "my-id", "stream:invalid-chunkSize", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ ok (stream == NULL,
+ "mongo_sync_gridfs_stream_find() should fail with invalid metadata");
+ bson_free (meta);
+
+ /* no-chunk test */
+ meta = bson_build (BSON_TYPE_STRING, "my-id", "stream:no-chunks", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ ok (stream != NULL,
+ "mongo_sync_gridfs_stream_find() works [stream:no-chunks]");
+ bson_free (meta);
+
+ r = mongo_sync_gridfs_stream_read (stream, data, sizeof (data));
+ cmp_ok (r, "==", -1,
+ "Reading from a chunk-less file should fail");
+
+ mongo_sync_gridfs_stream_close (stream);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_seek (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ bson *meta;
+ guint8 *chunk1, *chunk2, *chunk3;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ meta = bson_build (BSON_TYPE_STRING, "filename", "libmongo-test-stream", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ bson_free (meta);
+
+ chunk1 = g_malloc (300 * 1024);
+ chunk2 = g_malloc (300 * 1024);
+ chunk3 = g_malloc (300 * 1024);
+
+ cmp_ok (mongo_sync_gridfs_stream_read (stream, chunk1, 300 * 1024), "==",
+ 300 * 1024,
+ "reading the first chunk works");
+ cmp_ok (mongo_sync_gridfs_stream_read (stream, chunk2, 300 * 1024), "==",
+ 300 * 1024,
+ "reading the second chunk works");
+ ok (memcmp (chunk1, chunk2, 300 * 1024) != 0,
+ "The two chunks differ, as they should");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, 0, SEEK_END) == TRUE,
+ "mongo_sync_gridfs_stream_seek() works, with SEEK_END");
+ cmp_ok (stream->file.offset, "==", stream->file.length,
+ "mongo_sync_gridfs_stream_seek() can seek to the end");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, 1, SEEK_SET) == TRUE,
+ "mongo_sync_gridfs_stream_seek() works, with SEEK_SET");
+ cmp_ok (stream->file.offset, "==", 1,
+ "mongo_sync_gridfs_stream_seek()'s SEEK_SET works");
+ ok (mongo_sync_gridfs_stream_seek (stream, 1, SEEK_SET) == TRUE,
+ "mongo_sync_gridfs_stream_seek() works, with SEEK_SET");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, -1, SEEK_CUR) == TRUE,
+ "mongo_sync_gridfs_stream_seek() works, with SEEK_CUR");
+ cmp_ok (stream->file.offset, "==", 0,
+ "mongo_sync_gridfs_stream_seek()'s SEEK_CUR works");
+ ok (mongo_sync_gridfs_stream_seek (stream, 0, SEEK_CUR) == TRUE,
+ "mongo_sync_gridfs_stream_seek() works, with SEEK_CUR");
+
+ cmp_ok (mongo_sync_gridfs_stream_read (stream, chunk3, 300 * 1024), "==",
+ 300 * 1024,
+ "reading after seeking works");
+
+ ok (memcmp (chunk1, chunk3, 300 * 1024) == 0,
+ "After seeking, we're at the beginning");
+
+ mongo_sync_gridfs_stream_close (stream);
+ g_free (chunk3);
+ g_free (chunk2);
+ g_free (chunk1);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream_seek_invalid (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ bson *meta;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ meta = bson_build (BSON_TYPE_STRING, "my-id", "stream:no-chunks", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ stream = mongo_sync_gridfs_stream_find (gfs, meta);
+ bson_free (meta);
+
+ ok (mongo_sync_gridfs_stream_seek (stream, 1, SEEK_SET) == FALSE,
+ "mongo_sync_gridfs_stream_seek() should fail with no chunks");
+
+ mongo_sync_gridfs_stream_close (stream);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+}
+
+void
+test_func_sync_gridfs_stream (void)
+{
+ test_func_sync_gridfs_stream_without_oid_init ();
+
+ mongo_util_oid_init (0);
+
+ test_func_sync_gridfs_stream_write ();
+ test_func_sync_gridfs_stream_write_binary_subtype ();
+ test_func_sync_gridfs_stream_write_invalid ();
+ test_func_sync_gridfs_stream_read ();
+ test_func_sync_gridfs_stream_read_binary_subtype ();
+ test_func_sync_gridfs_stream_read_invalid ();
+ test_func_sync_gridfs_stream_seek ();
+ test_func_sync_gridfs_stream_seek_invalid ();
+ test_func_sync_gridfs_stream_meta ();
+
+ g_free (write_md5);
+}
+
+RUN_NET_TEST (38, func_sync_gridfs_stream);
diff --git a/tests/func/mongo/sync-pool/f_sync_pool.c b/tests/func/mongo/sync-pool/f_sync_pool.c
new file mode 100644
index 0000000..28a2497
--- /dev/null
+++ b/tests/func/mongo/sync-pool/f_sync_pool.c
@@ -0,0 +1,169 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+
+#include "libmongo-private.h"
+
+void
+test_func_mongo_sync_pool_secondary (void)
+{
+ mongo_sync_pool *pool;
+ mongo_sync_pool_connection *conn[11], *m, *s1, *s2, *t;
+ gint i = 0;
+ gboolean ret = TRUE;
+
+ skip (!config.secondary_host, 14,
+ "Secondary server not configured");
+
+ ok (mongo_sync_pool_new (config.secondary_host,
+ config.secondary_port, 1, 10) == NULL,
+ "mongo_sync_pool_new() should fail when connecting to a secondary");
+
+ pool = mongo_sync_pool_new (config.primary_host,
+ config.primary_port, 1, 10);
+ ok (pool != NULL,
+ "mongo_sync_pool_new() works with slaves too");
+
+ m = mongo_sync_pool_pick (pool, TRUE);
+ ok (m != NULL,
+ "mongo_sync_pool_pick() can pick a master from a mixed pool");
+ ok (mongo_sync_pool_pick (pool, TRUE) == NULL,
+ "mongo_sync_pool_pick() should fail if there are no more masters, and "
+ "a master was requested");
+
+ while ((conn[i] = mongo_sync_pool_pick (pool, FALSE)) != NULL)
+ i++;
+ cmp_ok (i, "==", 10,
+ "Successfully connect to secondaries on 10 sockets");
+ ok (mongo_sync_pool_pick (pool, FALSE) == NULL,
+ "mongo_sync_pool_pick() should fail if there are no free connections");
+
+ ok (mongo_sync_pool_return (pool, m) == TRUE,
+ "Returning the master to the pool works");
+
+ m = mongo_sync_pool_pick (pool, FALSE);
+ ok (m != NULL,
+ "mongo_sync_pool_pick() will return a master, if no more slaves are "
+ "available");
+
+ for (i = 0; i < 10; i++)
+ ret = ret && mongo_sync_pool_return (pool, conn[i]);
+
+ ok (ret == TRUE,
+ "mongo_sync_pool_return() works when returning slaves");
+
+ mongo_sync_pool_return (pool, m);
+
+ t = mongo_sync_pool_pick (pool, FALSE);
+ t->pool_id = 4242;
+
+ errno = 0;
+ ret = mongo_sync_pool_return (pool, t);
+ ok (ret == FALSE && errno == ERANGE,
+ "mongo_sync_pool_return() should fail if the connection ID is "
+ "out of range");
+
+ /* Test whether masters and slaves are different. */
+ m = mongo_sync_pool_pick (pool, TRUE);
+ s1 = mongo_sync_pool_pick (pool, FALSE);
+ s2 = mongo_sync_pool_pick (pool, FALSE);
+
+ ok (m != s1 && m != s2,
+ "Picked master and slaves are different");
+
+ ok (mongo_sync_cmd_is_master ((mongo_sync_connection *)m) == TRUE,
+ "Picked master is, indeed, a master");
+ ok (mongo_sync_cmd_is_master ((mongo_sync_connection *)s1) == FALSE,
+ "Picked secondary is a secondary");
+ ok (mongo_sync_cmd_is_master ((mongo_sync_connection *)s2) == FALSE,
+ "Picked secondary is a secondary");
+
+ mongo_sync_pool_free (pool);
+
+ endskip;
+}
+
+void
+test_func_mongo_sync_pool (void)
+{
+ mongo_sync_pool *pool;
+ mongo_sync_pool_connection *conn[11], *t;
+ gint c = 0;
+ gboolean ret = TRUE;
+ bson *b;
+ mongo_packet *p;
+
+ /*
+ * First we test that connecting to an invalid host fails.
+ */
+ pool = mongo_sync_pool_new ("invalid.example.com",
+ config.primary_port, 10, 10);
+ ok (pool == NULL,
+ "mongo_sync_pool_new() should fail with an invalid host");
+
+ /*
+ * Next, we test whether the basics work, like connecting, picking
+ * & returning.
+ */
+
+ pool = mongo_sync_pool_new (config.primary_host,
+ config.primary_port,
+ 10, 0);
+
+ ok (pool != NULL,
+ "mongo_sync_pool_new() works");
+
+ while ((conn[c] = mongo_sync_pool_pick (pool, TRUE)) != NULL)
+ c++;
+ cmp_ok (c, "==", 10,
+ "Successfully connect to the master on 10 sockets");
+
+ t = mongo_sync_pool_pick (pool, TRUE);
+ ok (t == NULL && errno == EAGAIN,
+ "Connected to the master only on 10 sockets");
+
+ for (c = 0; c < 10; c++)
+ ret = ret && mongo_sync_pool_return (pool, conn[c]);
+ ok (ret == TRUE,
+ "mongo_sync_pool_return() works");
+
+ t = mongo_sync_pool_pick (pool, TRUE);
+ ok (t != NULL,
+ "mongo_sync_pool_pick() works after returning connections");
+ mongo_sync_pool_return (pool, t);
+
+ /*
+ * Then we test whether we can perform commands on random
+ * connections.
+ */
+ conn[0] = mongo_sync_pool_pick (pool, TRUE);
+ conn[1] = mongo_sync_pool_pick (pool, TRUE);
+
+ ok (conn[0] != conn[1],
+ "Two picked connections are not the same");
+
+ b = bson_build (BSON_TYPE_STRING, "test-name", __FILE__, -1,
+ BSON_TYPE_INT32, "i32", 1984,
+ BSON_TYPE_NONE);
+ bson_finish (b);
+
+ ok (mongo_sync_cmd_insert ((mongo_sync_connection *)conn[0],
+ config.ns, b, NULL) == TRUE,
+ "mongo_sync_cmd_insert() works on a picked connection");
+
+ p = mongo_sync_cmd_query ((mongo_sync_connection *)conn[1],
+ config.ns, 0, 0, 1, b, NULL);
+ ok (p != NULL,
+ "mongo_sync_cmd_query() works on a different picked connection");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_pool_free (pool);
+
+ /*
+ * Test pools with a secondary aswell.
+ */
+ test_func_mongo_sync_pool_secondary ();
+}
+
+RUN_NET_TEST (23, func_mongo_sync_pool);
diff --git a/tests/func/mongo/sync/f_sync_auto_reauth.c b/tests/func/mongo/sync/f_sync_auto_reauth.c
new file mode 100644
index 0000000..477dd25
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_auto_reauth.c
@@ -0,0 +1,58 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "libmongo-private.h"
+
+
+/*
+ * This test requires that the "lmcUser" user (password "lmcPass") has
+ * RW access to the test db. It must be set up prior to running this
+ * test.
+ */
+void
+test_func_mongo_sync_auto_reauth (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+
+ b = bson_new ();
+ bson_append_int32 (b, "f_sync_auto_reauth", 1);
+ bson_finish (b);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+
+ mongo_sync_conn_set_safe_mode (conn, TRUE);
+
+ skip (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE, 3,
+ "Authentication not configured.");
+
+ skip (mongo_sync_cmd_authenticate (conn, config.db, "lmcUser", "lmcPass")== FALSE, 3,
+ "Authentication environment not set up for testing.");
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE,
+ "Inserting works after authentication.");
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (1);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == FALSE,
+ "Inserting fails with auto-reconnect turned off, and a broken "
+ "connection");
+
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE,
+ "Inserting works with auto-reconnect turned on, and auto-auth, "
+ "and a broken connection.");
+
+ endskip;
+ endskip;
+
+ mongo_sync_disconnect (conn);
+}
+
+RUN_NET_TEST (3, func_mongo_sync_auto_reauth);
diff --git a/tests/func/mongo/sync/f_sync_auto_reconnect.c b/tests/func/mongo/sync/f_sync_auto_reconnect.c
new file mode 100644
index 0000000..45ec28d
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_auto_reconnect.c
@@ -0,0 +1,61 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "libmongo-private.h"
+
+void
+test_func_mongo_sync_auto_reconnect (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+ mongo_packet *p;
+
+ b = bson_new ();
+ bson_append_int32 (b, "f_sync_auto_reconnect", 1);
+ bson_finish (b);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (1);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == FALSE,
+ "Inserting fails with auto-reconnect turned off, and a broken "
+ "connection");
+
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE,
+ "Inserting works with auto-reconnect turned on, and a broken "
+ "connection");
+
+ mongo_sync_conn_set_auto_reconnect (conn, FALSE);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (1);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == FALSE,
+ "Turning off auto-reconnect works");
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (1);
+
+ p = mongo_sync_cmd_query (conn, config.ns, 0, 0, 1, b, NULL);
+ ok (p == NULL,
+ "Query fails with auto-reconnect turned off");
+
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+ p = mongo_sync_cmd_query (conn, config.ns, 0, 0, 1, b, NULL);
+ ok (p != NULL,
+ "Query does reconnect with auto-reconnect turned on");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_disconnect (conn);
+}
+
+RUN_NET_TEST (6, func_mongo_sync_auto_reconnect);
diff --git a/tests/func/mongo/sync/f_sync_auto_reconnect_cache.c b/tests/func/mongo/sync/f_sync_auto_reconnect_cache.c
new file mode 100644
index 0000000..d69ea5d
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_auto_reconnect_cache.c
@@ -0,0 +1,107 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "libmongo-private.h"
+
+void
+test_func_mongo_sync_auto_reconnect_cache (void)
+{
+ mongo_sync_conn_recovery_cache *cache;
+ mongo_sync_connection *conn;
+ bson *b;
+ mongo_packet *p;
+ gchar *primary_addr;
+ const gchar *error_msg;
+
+ primary_addr = g_strdup_printf ("%s:%d", config.primary_host, config.primary_port);
+
+ b = bson_new ();
+ bson_append_int32 (b, "f_sync_auto_reconnect", 1);
+ bson_finish (b);
+
+ cache = mongo_sync_conn_recovery_cache_new ();
+
+ mongo_sync_conn_recovery_cache_seed_add (cache,
+ config.primary_host,
+ config.primary_port);
+
+ conn = mongo_sync_connect_recovery_cache (cache,
+ TRUE);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (1);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == FALSE,
+ "Inserting fails with auto-reconnect turned off, and a broken "
+ "connection");
+
+ error_msg = mongo_sync_conn_get_last_error (conn);
+
+ ok (error_msg != NULL, "We have an error msg when insert fails.");
+
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE,
+ "Inserting works with auto-reconnect turned on, and a broken "
+ "connection");
+
+ error_msg = mongo_sync_conn_get_last_error (conn);
+
+ ok (error_msg == NULL,
+ "After a succesful insert we shouldn't have an error msg.");
+
+ mongo_sync_conn_set_auto_reconnect (conn, FALSE);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (1);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == FALSE,
+ "Turning off auto-reconnect works");
+
+ skip (!config.secondary_host, 7,
+ "Secondary host not set up");
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (1);
+
+ p = mongo_sync_cmd_query (conn, config.ns, 0, 0, 1, b, NULL);
+ ok (p == NULL,
+ "Query fails with auto-reconnect turned off");
+
+ error_msg = mongo_sync_conn_get_last_error(conn);
+ ok (error_msg != NULL, "We have an error msg after a failure query.");
+
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+ p = mongo_sync_cmd_query (conn, config.ns, 0, 0, 1, b, NULL);
+ ok (p != NULL,
+ "Query does reconnect with auto-reconnect turned on");
+
+ ok (mongo_sync_conn_get_last_error(conn) == NULL,
+ "We shouldn't have any error messages after a successful operation.");
+
+ mongo_wire_packet_free (p);
+
+ mongo_sync_cmd_is_master (conn);
+
+ ok (conn->rs.hosts != NULL,
+ "We have hosts in the connection's replica set.");
+
+ ok (cache->rs.hosts == NULL, "Cache is empty.");
+
+ mongo_sync_disconnect (conn);
+
+ ok (cache->rs.hosts != NULL, "Cache is filled by disconnect()");
+
+ mongo_sync_conn_recovery_cache_free (cache);
+
+ endskip;
+
+ g_free (primary_addr);
+}
+
+RUN_NET_TEST (13, func_mongo_sync_auto_reconnect_cache);
diff --git a/tests/func/mongo/sync/f_sync_conn_seed_add.c b/tests/func/mongo/sync/f_sync_conn_seed_add.c
new file mode 100644
index 0000000..03bcdd2
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_conn_seed_add.c
@@ -0,0 +1,58 @@
+#include "test.h"
+#include <mongo.h>
+
+#include "libmongo-private.h"
+
+void
+test_func_mongo_sync_conn_seed_add (void)
+{
+ mongo_sync_connection *conn;
+ GList *l;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ FALSE);
+ close (conn->super.fd);
+
+ l = conn->rs.hosts;
+ while (l)
+ {
+ g_free (l->data);
+ l = g_list_delete_link (l, l);
+ }
+ conn->rs.hosts = NULL;
+
+ l = conn->rs.seeds;
+ while (l)
+ {
+ g_free (l->data);
+ l = g_list_delete_link (l, l);
+ }
+ conn->rs.seeds = NULL;
+
+ conn = mongo_sync_reconnect (conn, TRUE);
+ ok (conn == NULL,
+ "mongo_sync_reconnect() fails without seeds or discovery");
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ FALSE);
+ close (conn->super.fd);
+ l = conn->rs.hosts;
+ while (l)
+ {
+ g_free (l->data);
+ l = g_list_delete_link (l, l);
+ }
+ conn->rs.hosts = NULL;
+
+ ok (mongo_sync_conn_seed_add (conn, config.primary_host,
+ config.primary_port),
+ "mongo_sync_conn_seed_add() works");
+
+ conn = mongo_sync_reconnect (conn, TRUE);
+ ok (conn != NULL,
+ "mongo_sync_reconnect() works when properly seeded");
+
+ mongo_sync_disconnect (conn);
+}
+
+RUN_NET_TEST (3, func_mongo_sync_conn_seed_add);
diff --git a/tests/func/mongo/sync/f_sync_invalid_getlasterror.c b/tests/func/mongo/sync/f_sync_invalid_getlasterror.c
new file mode 100644
index 0000000..6af227b
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_invalid_getlasterror.c
@@ -0,0 +1,27 @@
+#include "test.h"
+#include <mongo.h>
+#include <errno.h>
+
+void
+test_func_mongo_sync_invalid_getlasterror (void)
+{
+ mongo_sync_connection *conn;
+ gchar *error = NULL;
+ gboolean res;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+
+ res = mongo_sync_cmd_get_last_error
+ (conn, "1234567890123456789012345678901234567890123456789012345678901234567890",
+ &error);
+
+ ok (res == FALSE,
+ "Trying to get the last error from an invalid DB results in an error.");
+ ok (error == NULL,
+ "When getLastError() fails, error remains NULL");
+
+ mongo_sync_disconnect (conn);
+}
+
+RUN_NET_TEST (2, func_mongo_sync_invalid_getlasterror);
diff --git a/tests/func/mongo/sync/f_sync_max_insert_size.c b/tests/func/mongo/sync/f_sync_max_insert_size.c
new file mode 100644
index 0000000..9ea5854
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_max_insert_size.c
@@ -0,0 +1,69 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+
+#include "libmongo-private.h"
+
+void
+test_func_mongo_sync_max_insert_size (void)
+{
+ mongo_sync_connection *conn;
+ const bson *docs[10];
+ bson *b1, *b2, *b3;
+
+ b1 = bson_new ();
+ bson_append_string (b1, "func_mongo_sync_max_insert_size", "works", -1);
+
+ bson_finish (b1);
+ b2 = bson_new ();
+ bson_append_int32 (b2, "int32", 1984);
+ bson_finish (b2);
+ b3 = bson_new ();
+ bson_finish (b3);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ FALSE);
+
+ /*
+ * cmd_insert_n()
+ */
+ mongo_sync_conn_set_max_insert_size (conn, bson_size (b1) +
+ bson_size (b3) + 1);
+
+ docs[0] = b1;
+ docs[1] = b2;
+ docs[2] = b3;
+
+ ok (mongo_sync_cmd_insert_n (conn, config.ns, 3, docs) == TRUE,
+ "mongo_sync_cmd_insert_n() works with a small max_insert_size");
+
+ mongo_sync_conn_set_max_insert_size (conn, 1);
+ errno = 0;
+ ok (mongo_sync_cmd_insert_n (conn, config.ns, 3, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() should fail if any one document is too big");
+ cmp_ok (errno, "==", EMSGSIZE,
+ "errno is set to EMSGSIZE");
+
+ /*
+ * cmd_insert()
+ */
+ mongo_sync_conn_set_max_insert_size (conn, bson_size (b1) +
+ bson_size (b3) + 1);
+ ok (mongo_sync_cmd_insert (conn, config.ns, b1, b2, b3, NULL) == TRUE,
+ "mongo_sync_cmd_insert() works with a small max_insert_size");
+
+ mongo_sync_conn_set_max_insert_size (conn, 1);
+ errno = 0;
+ ok (mongo_sync_cmd_insert (conn, config.ns, b1, b2, b3, NULL) == FALSE,
+ "mongo_sync_cmd_insert() should fail if any one document is too big");
+ cmp_ok (errno, "==", EMSGSIZE,
+ "errno is set to EMSGSIZE");
+
+ mongo_sync_disconnect (conn);
+ bson_free (b1);
+ bson_free (b2);
+ bson_free (b3);
+}
+
+RUN_NET_TEST (6, func_mongo_sync_max_insert_size);
diff --git a/tests/func/mongo/sync/f_sync_oidtest.c b/tests/func/mongo/sync/f_sync_oidtest.c
new file mode 100644
index 0000000..2a64692
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_oidtest.c
@@ -0,0 +1,44 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <string.h>
+
+void
+test_func_mongo_sync_oidtest (void)
+{
+ mongo_sync_connection *conn;
+ bson *boid, *reply = NULL;
+ bson_cursor *c;
+ mongo_packet *p;
+ guint8 *oid;
+ const guint8 *noid;
+
+ mongo_util_oid_init (0);
+
+ oid = mongo_util_oid_new (1);
+ boid = bson_new ();
+ bson_append_oid (boid, "driverOIDTest", oid);
+ bson_finish (boid);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ FALSE);
+
+ p = mongo_sync_cmd_custom (conn, config.db, boid);
+ ok (p != NULL,
+ "driverOIDTest(OID) custom command works");
+ mongo_wire_reply_packet_get_nth_document (p, 1, &reply);
+ bson_finish (reply);
+
+ c = bson_find (reply, "oid");
+ bson_cursor_get_oid (c, &noid);
+ ok (memcmp (oid, noid, 12) == 0,
+ "driverOIDTest(OID) returns the same OID");
+ bson_cursor_free (c);
+
+ mongo_sync_disconnect (conn);
+ mongo_wire_packet_free (p);
+ bson_free (boid);
+ bson_free (reply);
+}
+
+RUN_NET_TEST (2, func_mongo_sync_oidtest);
diff --git a/tests/func/mongo/sync/f_sync_safe_mode.c b/tests/func/mongo/sync/f_sync_safe_mode.c
new file mode 100644
index 0000000..e312c2f
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_safe_mode.c
@@ -0,0 +1,112 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "libmongo-private.h"
+
+void
+test_func_mongo_sync_safe_mode_basics (void)
+{
+ mongo_sync_connection *conn;
+ const bson *docs[10];
+ bson *b1, *b2, *b3, *b4, *cmd;
+ mongo_packet *p;
+ gchar *error;
+
+ mongo_util_oid_init (0);
+
+ b1 = bson_new ();
+ bson_append_string (b1, "func_mongo_sync_safe_mode", "works", -1);
+ bson_finish (b1);
+
+ b2 = bson_new ();
+ bson_append_int32 (b2, "int32", 1984);
+ bson_finish (b2);
+
+ b3 = test_bson_generate_full ();
+ b4 = test_bson_generate_full ();
+
+ docs[0] = b1;
+ docs[1] = b2;
+ docs[2] = b3;
+ docs[3] = b4;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ FALSE);
+
+ /* Test inserts */
+ mongo_sync_conn_set_safe_mode (conn, FALSE);
+ ok (mongo_sync_cmd_insert_n (conn, config.ns, 4, docs) == TRUE,
+ "mongo_sync_cmd_insert_n() should not fail with safe mode off");
+
+ mongo_sync_conn_set_safe_mode (conn, TRUE);
+ ok (mongo_sync_cmd_insert_n (conn, config.ns, 4, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() should fail with safe mode on");
+
+ /* Test a custom command */
+ cmd = bson_new ();
+ bson_append_int32 (cmd, "bogusCommand", 1);
+ bson_finish (cmd);
+
+ mongo_sync_cmd_reset_error (conn, config.db);
+ mongo_sync_conn_set_safe_mode (conn, FALSE);
+ p = mongo_sync_cmd_custom (conn, config.db, cmd);
+ mongo_sync_cmd_get_last_error (conn, config.db, &error);
+ ok (p == NULL && strcmp (error, "no such cmd: bogusCommand") == 0,
+ "mongo_sync_cmd_custom() with a bogus command fails with safe-mode off");
+ bson_free (cmd);
+
+ cmd = bson_new ();
+ bson_append_int32 (cmd, "bogusCommand2", 1);
+ bson_finish (cmd);
+ mongo_sync_cmd_reset_error (conn, config.db);
+ mongo_sync_conn_set_safe_mode (conn, TRUE);
+ p = mongo_sync_cmd_custom (conn, config.db, cmd);
+ mongo_sync_cmd_get_last_error (conn, config.db, &error);
+ ok (p == NULL && strcmp (error, "no such cmd: bogusCommand2") == 0,
+ "mongo_sync_cmd_custom() with a bogus command fails with safe-mode on");
+ bson_free (cmd);
+
+ mongo_sync_disconnect (conn);
+ bson_free (b1);
+ bson_free (b2);
+ bson_free (b3);
+ bson_free (b4);
+}
+
+#define INVALID_NS "1234567890123456789012345678901234567890123456789012345678901234567890.test"
+
+void
+test_func_mongo_sync_safe_mode_invalid_db (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+ const bson *docs[1];
+
+ b = bson_new ();
+ bson_append_int32 (b, "int32", 1984);
+ bson_finish (b);
+
+ docs[0] = b;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+ mongo_sync_conn_set_safe_mode (conn, TRUE);
+
+ ok (mongo_sync_cmd_insert_n (conn, INVALID_NS, 1, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() should fail with safe mode on and an invalid NS");
+
+ mongo_sync_disconnect (conn);
+ bson_free (b);
+}
+
+void
+test_func_mongo_sync_safe_mode (void)
+{
+ test_func_mongo_sync_safe_mode_basics ();
+ test_func_mongo_sync_safe_mode_invalid_db ();
+}
+
+RUN_NET_TEST (5, func_mongo_sync_safe_mode);
diff --git a/tests/func/mongo/sync/f_sync_safe_mode_cache.c b/tests/func/mongo/sync/f_sync_safe_mode_cache.c
new file mode 100644
index 0000000..082617f
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_safe_mode_cache.c
@@ -0,0 +1,131 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "libmongo-private.h"
+
+void
+test_func_mongo_sync_safe_mode_basics_cache (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_conn_recovery_cache *cache;
+
+ const bson *docs[10];
+ bson *b1, *b2, *b3, *b4, *cmd;
+ mongo_packet *p;
+ gchar *error;
+
+ mongo_util_oid_init (0);
+
+ b1 = bson_new ();
+ bson_append_string (b1, "func_mongo_sync_safe_mode", "works", -1);
+ bson_finish (b1);
+
+ b2 = bson_new ();
+ bson_append_int32 (b2, "int32", 1984);
+ bson_finish (b2);
+
+ b3 = test_bson_generate_full ();
+ b4 = test_bson_generate_full ();
+
+ docs[0] = b1;
+ docs[1] = b2;
+ docs[2] = b3;
+ docs[3] = b4;
+
+ cache = mongo_sync_conn_recovery_cache_new ();
+
+ mongo_sync_conn_recovery_cache_seed_add (cache,
+ config.primary_host,
+ config.primary_port);
+
+ conn = mongo_sync_connect_recovery_cache (cache, FALSE);
+
+ /* Test inserts */
+ mongo_sync_conn_set_safe_mode (conn, FALSE);
+ ok (mongo_sync_cmd_insert_n (conn, config.ns, 4, docs) == TRUE,
+ "mongo_sync_cmd_insert_n() should not fail with safe mode off");
+
+ mongo_sync_conn_set_safe_mode (conn, TRUE);
+ ok (mongo_sync_cmd_insert_n (conn, config.ns, 4, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() should fail with safe mode on");
+
+ /* Test a custom command */
+ cmd = bson_new ();
+ bson_append_int32 (cmd, "bogusCommand", 1);
+ bson_finish (cmd);
+
+ mongo_sync_cmd_reset_error (conn, config.db);
+ mongo_sync_conn_set_safe_mode (conn, FALSE);
+ p = mongo_sync_cmd_custom (conn, config.db, cmd);
+ mongo_sync_cmd_get_last_error (conn, config.db, &error);
+ ok (p == NULL && strcmp (error, "no such cmd: bogusCommand") == 0,
+ "mongo_sync_cmd_custom() with a bogus command fails with safe-mode off");
+ bson_free (cmd);
+ g_free (error);
+
+ cmd = bson_new ();
+ bson_append_int32 (cmd, "bogusCommand2", 1);
+ bson_finish (cmd);
+ mongo_sync_cmd_reset_error (conn, config.db);
+ mongo_sync_conn_set_safe_mode (conn, TRUE);
+ p = mongo_sync_cmd_custom (conn, config.db, cmd);
+ mongo_sync_cmd_get_last_error (conn, config.db, &error);
+ ok (p == NULL && strcmp (error, "no such cmd: bogusCommand2") == 0,
+ "mongo_sync_cmd_custom() with a bogus command fails with safe-mode on");
+ bson_free (cmd);
+ g_free (error);
+
+ mongo_sync_disconnect (conn);
+ mongo_sync_conn_recovery_cache_free (cache);
+
+ bson_free (b1);
+ bson_free (b2);
+ bson_free (b3);
+ bson_free (b4);
+}
+
+#define INVALID_NS "1234567890123456789012345678901234567890123456789012345678901234567890.test"
+
+void
+test_func_mongo_sync_safe_mode_invalid_db_cache (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_conn_recovery_cache *cache;
+ bson *b;
+ const bson *docs[1];
+
+ b = bson_new ();
+ bson_append_int32 (b, "int32", 1984);
+ bson_finish (b);
+
+ docs[0] = b;
+
+ cache = mongo_sync_conn_recovery_cache_new ();
+
+ mongo_sync_conn_recovery_cache_seed_add (cache,
+ config.primary_host,
+ config.primary_port);
+
+ conn = mongo_sync_connect_recovery_cache (cache, TRUE);
+
+ mongo_sync_conn_set_safe_mode (conn, TRUE);
+
+ ok (mongo_sync_cmd_insert_n (conn, INVALID_NS, 1, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() should fail with safe mode on and an invalid NS");
+
+ mongo_sync_disconnect (conn);
+ mongo_sync_conn_recovery_cache_free (cache);
+ bson_free (b);
+}
+
+void
+test_func_mongo_sync_safe_mode_cache (void)
+{
+ test_func_mongo_sync_safe_mode_basics_cache ();
+ test_func_mongo_sync_safe_mode_invalid_db_cache ();
+}
+
+RUN_NET_TEST (5, func_mongo_sync_safe_mode_cache);
diff --git a/tests/func/mongo/sync/f_sync_write_error.c b/tests/func/mongo/sync/f_sync_write_error.c
new file mode 100644
index 0000000..b6d4750
--- /dev/null
+++ b/tests/func/mongo/sync/f_sync_write_error.c
@@ -0,0 +1,52 @@
+#include "test.h"
+#include <mongo.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "libmongo-private.h"
+
+#define INVALID_NS "test.$Uncle$.Dagobert$"
+
+void
+test_func_mongo_sync_write_error (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+ const gchar *error_msg;
+
+ b = bson_new ();
+ bson_append_int32 (b, "f_sync_write_error", 1);
+ bson_finish (b);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE,
+ "Inserting works with correct namespace when safe mode is off");
+
+ ok (mongo_sync_cmd_insert (conn, INVALID_NS, b, NULL) == TRUE,
+ "Inserting works with invalid namespace when safe mode is off");
+
+ error_msg = mongo_sync_conn_get_last_error (conn);
+ ok (error_msg == NULL,
+ "When safe mode is off, there is no error msg, even if ns is invalid.");
+
+ ok (mongo_sync_conn_set_safe_mode (conn, TRUE) == TRUE,
+ "Setting safe mode works.");
+
+ ok (mongo_sync_cmd_insert (conn, config.ns, b, NULL) == TRUE,
+ "Inserting works with correct namespace when safe mode is on");
+
+ ok (mongo_sync_cmd_insert (conn, INVALID_NS, b, NULL) == FALSE,
+ "Inserting fails with invalid namespace when safe mode is on");
+
+ error_msg = mongo_sync_conn_get_last_error (conn);
+
+ ok (error_msg != NULL,
+ "Inserting failed in safe mode, so we should have an error msg");
+
+ mongo_sync_disconnect (conn);
+}
+
+RUN_NET_TEST (7, func_mongo_sync_write_error);
diff --git a/tests/libtap/Makefile.am b/tests/libtap/Makefile.am
new file mode 100644
index 0000000..271ade2
--- /dev/null
+++ b/tests/libtap/Makefile.am
@@ -0,0 +1,4 @@
+check_LTLIBRARIES = libtap.la
+libtap_la_SOURCES = tap.c tap.h test.h test.c
+libtap_la_CFLAGS = -I$(top_srcdir)/src/ @GLIB_CFLAGS@
+libtap_la_LIBADD = $(top_builddir)/src/libmongo-client.la @GLIB_LIBS@
diff --git a/tests/libtap/tap.c b/tests/libtap/tap.c
new file mode 100644
index 0000000..e73ae4a
--- /dev/null
+++ b/tests/libtap/tap.c
@@ -0,0 +1,298 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <glib.h>
+#include "tap.h"
+
+static int expected_tests = NO_PLAN;
+static int failed_tests;
+static int current_test;
+static char *todo_mesg;
+
+void
+plan (int tests) {
+ expected_tests = tests;
+ if (tests != NO_PLAN)
+ printf("1..%d\n", tests);
+}
+
+static char *
+vstrdupf (const char *fmt, va_list args) {
+ char *str;
+ int size;
+ va_list args2;
+ va_copy(args2, args);
+ if (!fmt)
+ fmt = "";
+ size = g_vsnprintf(NULL, 0, fmt, args2) + 2;
+ str = malloc(size);
+ vsprintf(str, fmt, args);
+ va_end(args2);
+ return str;
+}
+
+int
+vok_at_loc (const char *file, int line, int test, const char *fmt,
+ va_list args)
+{
+ char *name = vstrdupf(fmt, args);
+ printf("%sok %d", test ? "" : "not ", ++current_test);
+ if (*name)
+ printf(" - %s", name);
+ if (todo_mesg) {
+ printf(" # TODO");
+ if (*todo_mesg)
+ printf(" %s", todo_mesg);
+ }
+ printf("\n");
+ if (!test) {
+ if (*name)
+ diag(" Failed%s test '%s'\n at %s line %d.",
+ todo_mesg ? " (TODO)" : "", name, file, line);
+ else
+ diag(" Failed%s test at %s line %d.",
+ todo_mesg ? " (TODO)" : "", file, line);
+ if (!todo_mesg)
+ failed_tests++;
+ }
+ free(name);
+ return test;
+}
+
+int
+ok_at_loc (const char *file, int line, int test, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ return test;
+}
+
+static int
+mystrcmp (const char *a, const char *b) {
+ return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b);
+}
+
+#define eq(a, b) (!mystrcmp(a, b))
+#define ne(a, b) (mystrcmp(a, b))
+
+int
+is_at_loc (const char *file, int line, const char *got, const char *expected,
+ const char *fmt, ...)
+{
+ int test = eq(got, expected);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" got: '%s'", got);
+ diag(" expected: '%s'", expected);
+ }
+ return test;
+}
+
+int
+isnt_at_loc (const char *file, int line, const char *got, const char *expected,
+ const char *fmt, ...)
+{
+ int test = ne(got, expected);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" got: '%s'", got);
+ diag(" expected: anything else");
+ }
+ return test;
+}
+
+int
+cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b,
+ const char *fmt, ...)
+{
+ int test = eq(op, "||") ? a || b
+ : eq(op, "&&") ? a && b
+ : eq(op, "|") ? a | b
+ : eq(op, "^") ? a ^ b
+ : eq(op, "&") ? a & b
+ : eq(op, "==") ? a == b
+ : eq(op, "!=") ? a != b
+ : eq(op, "<") ? a < b
+ : eq(op, ">") ? a > b
+ : eq(op, "<=") ? a <= b
+ : eq(op, ">=") ? a >= b
+ : eq(op, "<<") ? a << b
+ : eq(op, ">>") ? a >> b
+ : eq(op, "+") ? a + b
+ : eq(op, "-") ? a - b
+ : eq(op, "*") ? a * b
+ : eq(op, "/") ? a / b
+ : eq(op, "%") ? a % b
+ : diag("unrecognized operator '%s'", op);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" %d", a);
+ diag(" %s", op);
+ diag(" %d", b);
+ }
+ return test;
+}
+
+static void
+vdiag_to_fh (FILE *fh, const char *fmt, va_list args) {
+ char *mesg, *line;
+ int i;
+ if (!fmt)
+ return;
+ mesg = vstrdupf(fmt, args);
+ line = mesg;
+ for (i = 0; *line; i++) {
+ char c = mesg[i];
+ if (!c || c == '\n') {
+ mesg[i] = '\0';
+ fprintf(fh, "# %s\n", line);
+ if (!c) break;
+ mesg[i] = c;
+ line = &mesg[i+1];
+ }
+ }
+ free(mesg);
+ return;
+}
+
+int
+diag (const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vdiag_to_fh(stderr, fmt, args);
+ va_end(args);
+ return 0;
+}
+
+int
+note (const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vdiag_to_fh(stdout, fmt, args);
+ va_end(args);
+ return 0;
+}
+
+int
+exit_status () {
+ int retval = 0;
+ if (expected_tests == NO_PLAN) {
+ printf("1..%d\n", current_test);
+ }
+ else if (current_test != expected_tests) {
+ diag("Looks like you planned %d test%s but ran %d.",
+ expected_tests, expected_tests > 1 ? "s" : "", current_test);
+ retval = 255;
+ }
+ if (failed_tests) {
+ diag("Looks like you failed %d test%s of %d run.",
+ failed_tests, failed_tests > 1 ? "s" : "", current_test);
+ if (expected_tests == NO_PLAN)
+ retval = failed_tests;
+ else
+ retval = expected_tests - current_test + failed_tests;
+ }
+ return retval;
+}
+
+void
+skippy (int n, const char *fmt, ...) {
+ char *why;
+ va_list args;
+ va_start(args, fmt);
+ why = vstrdupf(fmt, args);
+ va_end(args);
+ while (n --> 0) {
+ printf("ok %d ", ++current_test);
+ note("skip %s\n", why);
+ }
+ free(why);
+}
+
+void
+ctodo (int ignore __attribute__((unused)), const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ todo_mesg = vstrdupf(fmt, args);
+ va_end(args);
+}
+
+void
+cendtodo () {
+ free(todo_mesg);
+ todo_mesg = NULL;
+}
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#include <regex.h>
+
+#ifdef __APPLE__
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#ifndef MAP_ANON
+#define MAP_ANON MAP_ANONYMOUS
+#endif
+
+/* Create a shared memory int to keep track of whether a piece of code executed
+dies. to be used in the dies_ok and lives_ok macros */
+int
+tap_test_died (int status) {
+ static int *test_died = NULL;
+ int prev;
+ if (!test_died) {
+ test_died = (int *)mmap(0, sizeof (int), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ *test_died = 0;
+ }
+ prev = *test_died;
+ *test_died = status;
+ return prev;
+}
+
+int
+like_at_loc (int for_match, const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...)
+{
+ int test;
+ regex_t re;
+ int err = regcomp(&re, expected, REG_EXTENDED);
+ if (err) {
+ char errbuf[256];
+ regerror(err, &re, errbuf, sizeof errbuf);
+ fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n",
+ expected, errbuf, file, line);
+ exit(255);
+ }
+ err = regexec(&re, got, 0, NULL, 0);
+ regfree(&re);
+ test = for_match ? !err : err;
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ if (for_match) {
+ diag(" '%s'", got);
+ diag(" doesn't match: '%s'", expected);
+ }
+ else {
+ diag(" '%s'", got);
+ diag(" matches: '%s'", expected);
+ }
+ }
+ return test;
+}
+#endif
diff --git a/tests/libtap/tap.h b/tests/libtap/tap.h
new file mode 100644
index 0000000..3e841bc
--- /dev/null
+++ b/tests/libtap/tap.h
@@ -0,0 +1,85 @@
+#ifndef __TAP_H__
+#define __TAP_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define NO_PLAN -1
+#define ok(...) ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define pass(...) ok(1, ## __VA_ARGS__)
+#define fail(...) ok(0, ## __VA_ARGS__)
+#define is(...) is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define isnt(...) isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define cmp_ok(...) cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+
+int vok_at_loc (const char *file, int line, int test, const char *fmt,
+ va_list args);
+void plan (int tests);
+int ok_at_loc (const char *file, int line, int test, const char *fmt,
+ ...);
+int diag (const char *fmt, ...);
+int note (const char *fmt, ...);
+int exit_status (void);
+void skippy (int n, const char *fmt, ...);
+void ctodo (int ignore, const char *fmt, ...);
+void cendtodo (void);
+int is_at_loc (const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...);
+int isnt_at_loc (const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...);
+int cmp_ok_at_loc (const char *file, int line, int a, const char *op,
+ int b, const char *fmt, ...);
+
+#ifdef _WIN32
+#define like(...) skippy(1, "like is not implemented on MSWin32")
+#define unlike(...) like()
+#else
+#define like(...) like_at_loc(1, __FILE__, __LINE__, __VA_ARGS__, NULL)
+#define unlike(...) like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL)
+int like_at_loc (int for_match, const char *file, int line,
+ const char *got, const char *expected,
+ const char *fmt, ...);
+#endif
+
+#define skip(test, ...) do {if (test) {skippy(__VA_ARGS__, NULL); break;}
+#define endskip } while (0)
+
+#define todo(...) ctodo(0, ## __VA_ARGS__, NULL)
+#define endtodo cendtodo()
+
+#define dies_ok(code, ...) dies_ok_common(code, 1, ## __VA_ARGS__)
+#define lives_ok(code, ...) dies_ok_common(code, 0, ## __VA_ARGS__)
+
+#ifdef _WIN32
+#define dies_ok_common(...) \
+ skippy(1, "Death detection is not supported on MSWin32")
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+int tap_test_died (int status);
+#define dies_ok_common(code, for_death, ...) \
+ do { \
+ tap_test_died(1); \
+ int cpid = fork(); \
+ switch (cpid) { \
+ case -1: \
+ perror("fork error"); \
+ exit(EXIT_FAILURE); \
+ case 0: /* child */ \
+ close(1); close(2); \
+ code \
+ tap_test_died(0); \
+ exit(EXIT_SUCCESS); \
+ } \
+ if (waitpid(cpid, NULL, 0) < 0) { \
+ perror("waitpid error"); \
+ exit(EXIT_FAILURE); \
+ } \
+ int it_died = tap_test_died(0); \
+ if (!it_died) {code} \
+ ok(for_death ? it_died : !it_died, ## __VA_ARGS__); \
+ } while (0)
+#endif
+#endif
diff --git a/tests/libtap/test.c b/tests/libtap/test.c
new file mode 100644
index 0000000..979807f
--- /dev/null
+++ b/tests/libtap/test.c
@@ -0,0 +1,183 @@
+#include "test.h"
+#include "bson.h"
+#include "mongo-utils.h"
+
+#include <glib.h>
+#include <string.h>
+
+#ifndef HAVE_MSG_NOSIGNAL
+#include <signal.h>
+#endif
+
+
+func_config_t config;
+
+bson *
+test_bson_generate_full (void)
+{
+ bson *b, *d, *a, *scope;
+ guint8 oid[] = "1234567890ab";
+
+ a = bson_new ();
+ bson_append_int32 (a, "0", 32);
+ bson_append_int64 (a, "1", (gint64)-42);
+ bson_finish (a);
+
+ d = bson_new ();
+ bson_append_string (d, "name", "sub-document", -1);
+ bson_append_int32 (d, "answer", 42);
+ bson_finish (d);
+
+ scope = bson_new ();
+ bson_append_string (scope, "v", "hello world", -1);
+ bson_finish (scope);
+
+ b = bson_new ();
+ bson_append_double (b, "double", 3.14);
+ bson_append_string (b, "str", "hello world", -1);
+ bson_append_document (b, "doc", d);
+ bson_append_array (b, "array", a);
+ bson_append_binary (b, "binary0", BSON_BINARY_SUBTYPE_GENERIC,
+ (guint8 *)"foo\0bar", 7);
+ bson_append_oid (b, "_id", oid);
+ bson_append_boolean (b, "TRUE", FALSE);
+ bson_append_utc_datetime (b, "date", 1294860709000);
+ bson_append_timestamp (b, "ts", 1294860709000);
+ bson_append_null (b, "null");
+ bson_append_regex (b, "foobar", "s/foo.*bar/", "i");
+ bson_append_javascript (b, "alert", "alert (\"hello world!\");", -1);
+ bson_append_symbol (b, "sex", "Marilyn Monroe", -1);
+ bson_append_javascript_w_scope (b, "print", "alert (v);", -1, scope);
+ bson_append_int32 (b, "int32", 32);
+ bson_append_int64 (b, "int64", (gint64)-42);
+ bson_finish (b);
+
+ bson_free (d);
+ bson_free (a);
+ bson_free (scope);
+
+ return b;
+}
+
+mongo_packet *
+test_mongo_wire_generate_reply (gboolean valid, gint32 nreturn,
+ gboolean with_docs)
+{
+ mongo_reply_packet_header rh;
+ mongo_packet_header h;
+ mongo_packet *p;
+ guint8 *data;
+ gint data_size = sizeof (mongo_reply_packet_header);
+ bson *b1 = NULL, *b2 = NULL;
+
+ p = mongo_wire_packet_new ();
+
+ h.opcode = (valid) ? GINT32_TO_LE (1) : GINT32_TO_LE (42);
+ h.id = GINT32_TO_LE (1984);
+ h.resp_to = GINT32_TO_LE (42);
+ if (with_docs)
+ {
+ b1 = test_bson_generate_full ();
+ b2 = test_bson_generate_full ();
+ data_size += bson_size (b1) + bson_size (b2);
+ }
+ h.length = GINT32_TO_LE (sizeof (mongo_packet_header) + data_size);
+
+ mongo_wire_packet_set_header (p, &h);
+
+ data = g_try_malloc (data_size);
+
+ rh.flags = 0;
+ rh.cursor_id = GINT64_TO_LE ((gint64)12345);
+ rh.start = 0;
+ rh.returned = GINT32_TO_LE (nreturn);
+
+ memcpy (data, &rh, sizeof (mongo_reply_packet_header));
+ if (with_docs)
+ {
+ memcpy (data + sizeof (mongo_reply_packet_header),
+ bson_data (b1), bson_size (b1));
+ memcpy (data + sizeof (mongo_reply_packet_header) + bson_size (b1),
+ bson_data (b2), bson_size (b2));
+ }
+
+ mongo_wire_packet_set_data (p, data, data_size);
+ g_free (data);
+ bson_free (b1);
+ bson_free (b2);
+
+ return p;
+}
+
+mongo_sync_connection *
+test_make_fake_sync_conn (gint fd, gboolean slaveok)
+{
+ mongo_sync_connection *c;
+
+ c = g_try_new0 (mongo_sync_connection, 1);
+ if (!c)
+ return NULL;
+
+ c->super.fd = fd;
+ c->slaveok = slaveok;
+ c->safe_mode = FALSE;
+ c->auto_reconnect = FALSE;
+ c->max_insert_size = MONGO_SYNC_DEFAULT_MAX_INSERT_SIZE;
+
+ return c;
+}
+
+gboolean
+test_env_setup (void)
+{
+ config.primary_host = config.secondary_host = NULL;
+ config.primary_port = config.secondary_port = 27017;
+ config.db = g_strdup ("test");
+ config.coll = g_strdup ("libmongo");
+
+ if (getenv ("TEST_DB"))
+ {
+ g_free (config.db);
+ config.db = g_strdup (getenv ("TEST_DB"));
+ }
+ if (getenv ("TEST_COLLECTION"))
+ {
+ g_free (config.coll);
+ config.coll = g_strdup (getenv ("TEST_COLLECTION"));
+ }
+ config.ns = g_strconcat (config.db, ".", config.coll, NULL);
+
+ config.gfs_prefix = g_strconcat (config.ns, ".", "grid", NULL);
+
+ if (!getenv ("TEST_PRIMARY") || strlen (getenv ("TEST_PRIMARY")) == 0)
+ return FALSE;
+
+ if (!mongo_util_parse_addr (getenv ("TEST_PRIMARY"), &config.primary_host,
+ &config.primary_port))
+ return FALSE;
+
+ if (getenv ("TEST_SECONDARY") && strlen (getenv ("TEST_SECONDARY")) > 0)
+ mongo_util_parse_addr (getenv ("TEST_SECONDARY"), &config.secondary_host,
+ &config.secondary_port);
+
+ return TRUE;
+}
+
+void
+test_env_free (void)
+{
+ g_free (config.primary_host);
+ g_free (config.secondary_host);
+ g_free (config.db);
+ g_free (config.coll);
+ g_free (config.ns);
+ g_free (config.gfs_prefix);
+}
+
+void
+test_main_setup (void)
+{
+ #ifndef HAVE_MSG_NOSIGNAL
+ signal(SIGPIPE, SIG_IGN);
+ #endif
+}
diff --git a/tests/libtap/test.h b/tests/libtap/test.h
new file mode 100644
index 0000000..1c442f5
--- /dev/null
+++ b/tests/libtap/test.h
@@ -0,0 +1,84 @@
+#ifndef LIBMONGO_CLIENT_TEST_H
+#define LIBMONGO_CLIENT_TEST_H 1
+
+#include "tap.h"
+#include "bson.h"
+#include "mongo-wire.h"
+#include "mongo-sync.h"
+#include "libmongo-private.h"
+
+#include <dlfcn.h>
+
+typedef struct
+{
+ gchar *primary_host;
+ gint primary_port;
+
+ gchar *secondary_host;
+ gint secondary_port;
+
+ gchar *db;
+ gchar *coll;
+ gchar *ns;
+
+ gchar *gfs_prefix;
+} func_config_t;
+
+extern func_config_t config;
+
+#define begin_network_tests(n) \
+ do \
+ { \
+ skip(!test_env_setup (), n, "Environment not set up for network tests")
+
+#define end_network_tests() \
+ endskip; \
+ test_env_free(); \
+ } while (0)
+
+#define RUN_TEST(n, t) \
+ int \
+ main (void) \
+ { \
+ test_main_setup(); \
+ plan (n); \
+ test_##t (); \
+ return 0; \
+ }
+
+gboolean test_env_setup (void);
+void test_env_free (void);
+void test_main_setup (void);
+
+#define RUN_NET_TEST(n, t) \
+ int \
+ main (void) \
+ { \
+ test_main_setup(); \
+ if (!test_env_setup ()) \
+ printf ("1..0 # skip, Environment not set up for network tests"); \
+ else \
+ { \
+ plan (n); \
+ test_##t (); \
+ } \
+ test_env_free (); \
+ return 0; \
+ }
+
+bson *test_bson_generate_full (void);
+mongo_packet *test_mongo_wire_generate_reply (gboolean valid,
+ gint32 nreturn,
+ gboolean with_docs);
+mongo_sync_connection *test_make_fake_sync_conn (gint fd,
+ gboolean slaveok);
+
+#define SAVE_OLD_FUNC(n) \
+ static void *(*func_##n)(); \
+ if (!func_##n) \
+ func_##n = (void *(*)())dlsym (RTLD_NEXT, #n);
+
+#define CALL_OLD_FUNC(n, ...) \
+ func_##n (__VA_ARGS__)
+
+#endif
diff --git a/tests/perf/bson/p_bson_find.c b/tests/perf/bson/p_bson_find.c
new file mode 100644
index 0000000..4e62132
--- /dev/null
+++ b/tests/perf/bson/p_bson_find.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+
+#include <mongo.h>
+
+#define MAX_KEYS 10000
+
+void
+test_p_bson_find (void)
+{
+ bson *b;
+ bson_cursor *c;
+ gint i;
+ gchar **keys;
+ gboolean ret = TRUE;
+
+ keys = g_new(gchar *, MAX_KEYS);
+
+ b = bson_new ();
+ for (i = 0; i < MAX_KEYS; i++)
+ {
+ keys[i] = g_strdup_printf ("tmp_key_%d", i);
+ bson_append_int32 (b, keys[i], i);
+ }
+ bson_finish (b);
+
+ for (i = 1; i <= MAX_KEYS; i++)
+ {
+ c = bson_find (b, keys[i - 1]);
+ if (!c)
+ ret = FALSE;
+ bson_cursor_free (c);
+ g_free (keys[i - 1]);
+ }
+
+ bson_free (b);
+ g_free (keys);
+
+ ok (ret == TRUE,
+ "bson_find() performance test ok");
+}
+
+RUN_TEST (1, p_bson_find);
diff --git a/tests/runall b/tests/runall
new file mode 100755
index 0000000..e25f8ef
--- /dev/null
+++ b/tests/runall
@@ -0,0 +1,17 @@
+#! /bin/sh
+set -e
+
+fail_counter=0
+
+trap '[ $? -eq 0 ] && rm -f current-test.out current-test.err' 0
+
+for test in $@; do
+ ${test} 2>current-test.err >current-test.out
+ if grep "not ok" current-test.out >/dev/null 2>/dev/null; then
+ echo "${test} failed:" >&2
+ cat current-test.err >&2
+ fail_counter=`expr ${fail_counter} + 1`
+ fi
+done
+
+exit ${fail_counter}
diff --git a/tests/test_cleanup.c b/tests/test_cleanup.c
new file mode 100644
index 0000000..bef203a
--- /dev/null
+++ b/tests/test_cleanup.c
@@ -0,0 +1,31 @@
+#include "test.h"
+#include <mongo.h>
+
+int
+main (void)
+{
+ mongo_sync_connection *conn;
+ gchar *c;
+
+ if (!test_env_setup ())
+ return 0;
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ mongo_sync_cmd_drop (conn, config.db, config.coll);
+
+ c = g_strconcat (config.coll, ".grid.files", NULL);
+ mongo_sync_cmd_drop (conn, config.db, c);
+ g_free (c);
+
+ c = g_strconcat (config.coll, ".grid.chunks", NULL);
+ mongo_sync_cmd_drop (conn, config.db, c);
+ g_free (c);
+
+ c = g_strconcat (config.coll, ".capped", NULL);
+ mongo_sync_cmd_drop (conn, config.db, c);
+ g_free (c);
+
+ test_env_free ();
+
+ return 0;
+}
diff --git a/tests/tools/coverage-report-entry.pl b/tests/tools/coverage-report-entry.pl
new file mode 100644
index 0000000..53ec17b
--- /dev/null
+++ b/tests/tools/coverage-report-entry.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2006 Daniel Berrange
+#
+# 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; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+print <<EOF;
+<html>
+<head>
+<title>Coverage report for $ARGV[0]</title>
+<style type="text/css">
+ span.perfect {
+ background: rgb(0,255,0);
+ }
+ span.terrible {
+ background: rgb(255,0,0);
+ }
+</style>
+</head>
+<body>
+<h1>Coverage report for $ARGV[0]</h1>
+
+<pre>
+EOF
+
+
+while (<>) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+
+ if (/^\s*function (\S+) called (\d+) returned \d+% blocks executed \d+%/) {
+ my $class = $2 > 0 ? "perfect" : "terrible";
+ $_ = "<span class=\"$class\" id=\"" . $1 . "\">$_</span>";
+ } elsif (/^\s*branch\s+\d+\s+taken\s+(\d+)%\s+.*$/) {
+ my $class = $1 > 0 ? "perfect" : "terrible";
+ $_ = "<span class=\"$class\">$_</span>";
+ } elsif (/^\s*branch\s+\d+\s+never executed.*$/) {
+ my $class = "terrible";
+ $_ = "<span class=\"$class\">$_</span>";
+ } elsif (/^\s*call\s+\d+\s+never executed.*$/) {
+ my $class = "terrible";
+ $_ = "<span class=\"$class\">$_</span>";
+ } elsif (/^\s*call\s+\d+\s+returned\s+(\d+)%.*$/) {
+ my $class = $1 > 0 ? "perfect" : "terrible";
+ $_ = "<span class=\"$class\">$_</span>";
+ }
+
+
+ print;
+}
+
+print <<EOF;
+</pre>
+</body>
+</html>
+EOF
diff --git a/tests/tools/coverage-report.pl b/tests/tools/coverage-report.pl
new file mode 100644
index 0000000..6ad7abd
--- /dev/null
+++ b/tests/tools/coverage-report.pl
@@ -0,0 +1,125 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2006 Daniel Berrange
+#
+# 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; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+use warnings;
+use strict;
+
+my %coverage = ( functions => {}, files => {} );
+
+my %filemap;
+
+my $type;
+my $name;
+
+my @functions;
+
+while (<>) {
+ if (/^Function '(.*)'\s*$/) {
+ $type = "function";
+ $name = $1;
+ $coverage{$type}->{$name} = {};
+ push @functions, $name;
+ } elsif (/^File '(.*?)'\s*$/) {
+ $type = "file";
+ $name = $1;
+ $coverage{$type}->{$name} = {};
+
+ foreach my $func (@functions) {
+ $coverage{"function"}->{$func}->{file} = $name;
+ }
+ @functions = ();
+ } elsif (/^Lines executed:(.*)%\s*of\s*(\d+)\s*$/) {
+ $coverage{$type}->{$name}->{lines} = $2;
+ $coverage{$type}->{$name}->{linesCoverage} = $1;
+ } elsif (/^Branches executed:(.*)%\s*of\s*(\d+)\s*$/) {
+ $coverage{$type}->{$name}->{branches} = $2;
+ $coverage{$type}->{$name}->{branchesCoverage} = $1;
+ } elsif (/^Taken at least once:(.*)%\s*of\s*(\d+)\s*$/) {
+ $coverage{$type}->{$name}->{conds} = $2;
+ $coverage{$type}->{$name}->{condsCoverage} = $1;
+ } elsif (/^Calls executed:(.*)%\s*of\s*(\d+)\s*$/) {
+ $coverage{$type}->{$name}->{calls} = $2;
+ $coverage{$type}->{$name}->{callsCoverage} = $1;
+ } elsif (/^No branches$/) {
+ $coverage{$type}->{$name}->{branches} = 0;
+ $coverage{$type}->{$name}->{branchesCoverage} = "100.00";
+ $coverage{$type}->{$name}->{conds} = 0;
+ $coverage{$type}->{$name}->{condsCoverage} = "100.00";
+ } elsif (/^No calls$/) {
+ $coverage{$type}->{$name}->{calls} = 0;
+ $coverage{$type}->{$name}->{callsCoverage} = "100.00";
+ } elsif (/^\s*(.*):creating '(.*)'\s*$/) {
+ $filemap{$1} = $2;
+ } elsif (/^\s*$/) {
+ # nada
+ } else {
+ warn "Shit [$_]\n";
+ }
+}
+
+my %summary;
+foreach my $type ("function", "file") {
+ $summary{$type} = {};
+ foreach my $m ("lines", "branches", "conds", "calls") {
+ my $totalGot = 0;
+ my $totalMiss = 0;
+ my $count = 0;
+ foreach my $func (keys %{$coverage{function}}) {
+ $count++;
+ my $got = $coverage{function}->{$func}->{$m};
+ $totalGot += $got;
+ my $miss = $got * $coverage{function}->{$func}->{$m ."Coverage"} / 100;
+ $totalMiss += $miss;
+ }
+ $summary{$type}->{$m} = sprintf("%d", $totalGot);
+ $summary{$type}->{$m . "Coverage"} = sprintf("%.2f", $totalMiss / $totalGot * 100);
+ }
+}
+
+
+
+print "<coverage>\n";
+
+foreach my $type ("function", "file") {
+ printf "<%ss>\n", $type;
+ foreach my $name (sort { $a cmp $b } keys %{$coverage{$type}}) {
+ my $rec = $coverage{$type}->{$name};
+ printf " <entry name=\"%s\" details=\"%s\">\n", $name, ($type eq "file" ? $filemap{$name} : $filemap{$rec->{file}});
+ printf " <lines count=\"%s\" coverage=\"%s\"/>\n", $rec->{lines}, $rec->{linesCoverage};
+ if (exists $rec->{branches}) {
+ printf " <branches count=\"%s\" coverage=\"%s\"/>\n", $rec->{branches}, $rec->{branchesCoverage};
+ }
+ if (exists $rec->{conds}) {
+ printf " <conditions count=\"%s\" coverage=\"%s\"/>\n", $rec->{conds}, $rec->{condsCoverage};
+ }
+ if (exists $rec->{calls}) {
+ printf " <calls count=\"%s\" coverage=\"%s\"/>\n", $rec->{calls}, $rec->{callsCoverage};
+ }
+ print " </entry>\n";
+ }
+
+ printf " <summary>\n";
+ printf " <lines count=\"%s\" coverage=\"%s\"/>\n", $summary{$type}->{lines}, $summary{$type}->{linesCoverage};
+ printf " <branches count=\"%s\" coverage=\"%s\"/>\n", $summary{$type}->{branches}, $summary{$type}->{branchesCoverage};
+ printf " <conditions count=\"%s\" coverage=\"%s\"/>\n", $summary{$type}->{conds}, $summary{$type}->{condsCoverage};
+ printf " <calls count=\"%s\" coverage=\"%s\"/>\n", $summary{$type}->{calls}, $summary{$type}->{callsCoverage};
+ printf " </summary>\n";
+ printf "</%ss>\n", $type;
+}
+
+print "</coverage>\n";
diff --git a/tests/tools/coverage-report.xsl b/tests/tools/coverage-report.xsl
new file mode 100644
index 0000000..ca3f57f
--- /dev/null
+++ b/tests/tools/coverage-report.xsl
@@ -0,0 +1,235 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+#
+# Copyright (C) 2006 Daniel Berrange
+#
+# 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; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+ <xsl:output method="html"/>
+
+ <xsl:template match="coverage">
+ <html>
+ <head>
+ <title>Coverage report</title>
+ <style type="text/css">
+ tbody tr.odd td.label {
+ border-top: 1px solid rgb(128,128,128);
+ border-bottom: 1px solid rgb(128,128,128);
+ }
+ tbody tr.odd td.label {
+ background: rgb(200,200,200);
+ }
+
+ thead, tfoot {
+ background: rgb(60,60,60);
+ color: white;
+ font-weight: bold;
+ }
+
+ tr td.perfect {
+ background: rgb(0,255,0);
+ color: black;
+ }
+ tr td.excellant {
+ background: rgb(140,255,140);
+ color: black;
+ }
+ tr td.good {
+ background: rgb(160,255,0);
+ color: black;
+ }
+ tr td.poor {
+ background: rgb(255,160,0);
+ color: black;
+ }
+ tr td.bad {
+ background: rgb(255,140,140);
+ color: black;
+ }
+ tr td.terrible {
+ background: rgb(255,0,0);
+ color: black;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Coverage report</h1>
+ <xsl:apply-templates/>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="functions">
+ <h2>Function coverage</h2>
+ <xsl:call-template name="content">
+ <xsl:with-param name="type" select="'function'"/>
+ </xsl:call-template>
+ </xsl:template>
+
+
+ <xsl:template match="files">
+ <h2>File coverage</h2>
+ <xsl:call-template name="content">
+ <xsl:with-param name="type" select="'file'"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="content">
+ <xsl:param name="type"/>
+ <table>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Lines</th>
+ <th>Branches</th>
+ <th>Conditions</th>
+ <th>Calls</th>
+ </tr>
+ </thead>
+ <tbody>
+ <xsl:for-each select="entry">
+ <xsl:call-template name="entry">
+ <xsl:with-param name="type" select="$type"/>
+ <xsl:with-param name="class">
+ <xsl:choose>
+ <xsl:when test="position() mod 2">
+ <xsl:text>odd</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>even</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+ </tbody>
+ <tfoot>
+ <xsl:for-each select="summary">
+ <xsl:call-template name="entry">
+ <xsl:with-param name="type" select="'summary'"/>
+ <xsl:with-param name="class">
+ <xsl:choose>
+ <xsl:when test="position() mod 2">
+ <xsl:text>odd</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>even</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+ </tfoot>
+ </table>
+ </xsl:template>
+
+ <xsl:template name="entry">
+ <xsl:param name="type"/>
+ <xsl:param name="class"/>
+ <tr class="{$class}">
+ <xsl:choose>
+ <xsl:when test="$type = 'function'">
+ <td class="label"><a href="{@details}.html#{@name}"><xsl:value-of select="@name"/></a></td>
+ </xsl:when>
+ <xsl:when test="$type = 'file'">
+ <td class="label"><a href="{@details}.html"><xsl:value-of select="@name"/></a></td>
+ </xsl:when>
+ <xsl:otherwise>
+ <td class="label">Summary</td>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:if test="count(lines)">
+ <xsl:apply-templates select="lines"/>
+ </xsl:if>
+ <xsl:if test="not(count(lines))">
+ <xsl:call-template name="missing"/>
+ </xsl:if>
+
+ <xsl:if test="count(branches)">
+ <xsl:apply-templates select="branches"/>
+ </xsl:if>
+ <xsl:if test="not(count(branches))">
+ <xsl:call-template name="missing"/>
+ </xsl:if>
+
+ <xsl:if test="count(conditions)">
+ <xsl:apply-templates select="conditions"/>
+ </xsl:if>
+ <xsl:if test="not(count(conditions))">
+ <xsl:call-template name="missing"/>
+ </xsl:if>
+
+ <xsl:if test="count(calls)">
+ <xsl:apply-templates select="calls"/>
+ </xsl:if>
+ <xsl:if test="not(count(calls))">
+ <xsl:call-template name="missing"/>
+ </xsl:if>
+
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="lines">
+ <xsl:call-template name="row"/>
+ </xsl:template>
+
+ <xsl:template match="branches">
+ <xsl:call-template name="row"/>
+ </xsl:template>
+
+ <xsl:template match="conditions">
+ <xsl:call-template name="row"/>
+ </xsl:template>
+
+ <xsl:template match="calls">
+ <xsl:call-template name="row"/>
+ </xsl:template>
+
+ <xsl:template name="missing">
+ <td></td>
+ </xsl:template>
+
+ <xsl:template name="row">
+ <xsl:variable name="quality">
+ <xsl:choose>
+ <xsl:when test="@coverage = 100">
+ <xsl:text>perfect</xsl:text>
+ </xsl:when>
+ <xsl:when test="@coverage >= 80.0">
+ <xsl:text>excellant</xsl:text>
+ </xsl:when>
+ <xsl:when test="@coverage >= 60.0">
+ <xsl:text>good</xsl:text>
+ </xsl:when>
+ <xsl:when test="@coverage >= 40.0">
+ <xsl:text>poor</xsl:text>
+ </xsl:when>
+ <xsl:when test="@coverage >= 20.0">
+ <xsl:text>bad</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>terrible</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <td class="{$quality}"><xsl:value-of select="@coverage"/>% of <xsl:value-of select="@count"/></td>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/tests/unit/bson/bson_append_array.c b/tests/unit/bson/bson_append_array.c
new file mode 100644
index 0000000..040f0ed
--- /dev/null
+++ b/tests/unit/bson/bson_append_array.c
@@ -0,0 +1,65 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_array (void)
+{
+ bson *b, *e1, *e2;
+
+ e1 = bson_new ();
+ bson_append_int32 (e1, "0", 1984);
+ bson_append_string (e1, "1", "hello world", -1);
+ bson_finish (e1);
+
+ e2 = bson_new ();
+ bson_append_string (e2, "0", "bar", -1);
+ ok (bson_append_array (e2, "1", e1),
+ "bson_append_array() works");
+ bson_finish (e2);
+ bson_free (e1);
+
+ b = bson_new ();
+ ok (bson_append_array (b, "0", e2),
+ "bson_append_array() works still");
+ bson_finish (b);
+ bson_free (e2);
+
+ cmp_ok (bson_size (b), "==", 58, "BSON array element size check");
+ ok (memcmp (bson_data (b),
+ "\072\000\000\000\004\060\000\062\000\000\000\002\060\000\004"
+ "\000\000\000\142\141\162\000\004\061\000\037\000\000\000\020"
+ "\060\000\300\007\000\000\002\061\000\014\000\000\000\150\145"
+ "\154\154\157\040\167\157\162\154\144\000\000\000\000",
+ bson_size (b)) == 0,
+ "BSON array element contents check");
+
+ bson_free (b);
+
+ e1 = bson_new ();
+ bson_append_int32 (e1, "0", 1984);
+ b = bson_new ();
+
+ ok (bson_append_array (b, "array", e1) == FALSE,
+ "bson_append_array() with an unfinished array should fail");
+ bson_finish (e1);
+ ok (bson_append_array (b, NULL, e1) == FALSE,
+ "bson_append_array() with a NULL name should fail");
+ ok (bson_append_array (b, "foo", NULL) == FALSE,
+ "bson_append_array() with a NULL array should fail");
+ ok (bson_append_array (NULL, "foo", e1) == FALSE,
+ "bson_append_array() with a NULL BSON should fail");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_array (b, "array", e1) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (e1);
+ bson_free (b);
+}
+
+RUN_TEST (10, bson_array);
diff --git a/tests/unit/bson/bson_append_binary.c b/tests/unit/bson/bson_append_binary.c
new file mode 100644
index 0000000..06ea362
--- /dev/null
+++ b/tests/unit/bson/bson_append_binary.c
@@ -0,0 +1,56 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_binary (void)
+{
+ bson *b;
+
+ b = bson_new ();
+ ok (bson_append_binary (b, "binary0", BSON_BINARY_SUBTYPE_GENERIC,
+ (guint8 *)"foo\0bar", 7),
+ "bson_append_binary(), type 0 works");
+ ok (bson_append_binary (b, "binary2", BSON_BINARY_SUBTYPE_BINARY,
+ (guint8 *)"\0\0\0\7foo\0bar", 11),
+ "bson_append_binary(), type 2 works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 51, "BSON binary element size check");
+ ok (memcmp (bson_data (b),
+ "\063\000\000\000\005\142\151\156\141\162\171\060\000\007\000"
+ "\000\000\000\146\157\157\000\142\141\162\005\142\151\156\141"
+ "\162\171\062\000\013\000\000\000\002\000\000\000\007\146\157"
+ "\157\000\142\141\162\000",
+ bson_size (b)) == 0,
+ "BSON binary element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_binary (b, NULL, BSON_BINARY_SUBTYPE_GENERIC,
+ (guint8 *)"foo\0bar", 7) == FALSE,
+ "bson_append_binary() without a key name should fail");
+ ok (bson_append_binary (b, "binary1", BSON_BINARY_SUBTYPE_GENERIC,
+ NULL, 10) == FALSE,
+ "bson_append_binary () without binary data should fail");
+ ok (bson_append_binary (b, "binary3", BSON_BINARY_SUBTYPE_GENERIC,
+ (guint8 *)"foo\0bar", -1) == FALSE,
+ "bson_append_binary () with an invalid length should fail");
+ ok (bson_append_binary (NULL, "binary1", BSON_BINARY_SUBTYPE_GENERIC,
+ (guint8 *)"foo\0bar", 7) == FALSE,
+ "bson_append_binary () without a BSON object should fail");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_binary (b, "binary", BSON_BINARY_SUBTYPE_GENERIC,
+ (guint8 *)"foo\0bar", 7) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (10, bson_binary);
diff --git a/tests/unit/bson/bson_append_boolean.c b/tests/unit/bson/bson_append_boolean.c
new file mode 100644
index 0000000..03df0a5
--- /dev/null
+++ b/tests/unit/bson/bson_append_boolean.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_boolean (void)
+{
+ bson *b;
+
+ b = bson_new ();
+ ok (bson_append_boolean (b, "FALSE", FALSE),
+ "bson_append_boolean() works");
+ ok (bson_append_boolean (b, "TRUE", TRUE),
+ "bson_append_boolean() works still");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 20, "BSON boolean element size check");
+ ok (memcmp (bson_data (b),
+ "\024\000\000\000\010\106\101\114\123\105\000\000\010\124\122"
+ "\125\105\000\001\000",
+ bson_size (b)) == 0,
+ "BSON boolean element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_boolean (b, NULL, TRUE) == FALSE,
+ "bson_append_boolean() with a NULL key should fail");
+ ok (bson_append_boolean (NULL, "TRUE", TRUE) == FALSE,
+ "bson_append_boolean() without a BSON object should fail");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_boolean (b, "b", TRUE) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (8, bson_boolean);
diff --git a/tests/unit/bson/bson_append_document.c b/tests/unit/bson/bson_append_document.c
new file mode 100644
index 0000000..04d077c
--- /dev/null
+++ b/tests/unit/bson/bson_append_document.c
@@ -0,0 +1,67 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_document (void)
+{
+ bson *b, *e1, *e2;
+
+ e1 = bson_new ();
+ bson_append_int32 (e1, "i32", 1984);
+ bson_append_string (e1, "str", "hello world", -1);
+ bson_finish (e1);
+
+ e2 = bson_new ();
+ bson_append_string (e2, "foo", "bar", -1);
+ ok (bson_append_document (e2, "subd", e1),
+ "bson_append_document() works");
+ bson_finish (e2);
+ bson_free (e1);
+
+ b = bson_new ();
+ ok (bson_append_document (b, "doc", e2),
+ "bson_append_document() works still");
+ bson_finish (b);
+ bson_free (e2);
+
+ cmp_ok (bson_size (b), "==", 69, "BSON document element size check");
+ ok (memcmp (bson_data (b),
+ "\105\000\000\000\003\144\157\143\000\073\000\000\000\002\146"
+ "\157\157\000\004\000\000\000\142\141\162\000\003\163\165\142"
+ "\144\000\043\000\000\000\020\151\063\062\000\300\007\000\000"
+ "\002\163\164\162\000\014\000\000\000\150\145\154\154\157\040"
+ "\167\157\162\154\144\000\000\000\000",
+ bson_size (b)) == 0,
+ "BSON document element contents check");
+
+ bson_free (b);
+
+ e1 = bson_new ();
+ bson_append_int32 (e1, "foo", 42);
+ b = bson_new ();
+
+ ok (bson_append_document (b, "doc", e1) == FALSE,
+ "bson_append_document() with an unfinished document should fail");
+ bson_finish (e1);
+ ok (bson_append_document (b, NULL, e1) == FALSE,
+ "bson_append_document() with a NULL key should fail");
+ ok (bson_append_document (b, "doc", NULL) == FALSE,
+ "bson_append_document() with a NULL document should fail");
+ ok (bson_append_document (NULL, "doc", e1) == FALSE,
+ "bson_append_document() without a BSON object should fail");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_document (b, "doc", e1) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (e1);
+ bson_free (b);
+}
+
+RUN_TEST (10, bson_document);
diff --git a/tests/unit/bson/bson_append_double.c b/tests/unit/bson/bson_append_double.c
new file mode 100644
index 0000000..de62c15
--- /dev/null
+++ b/tests/unit/bson/bson_append_double.c
@@ -0,0 +1,41 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_double (void)
+{
+ bson *b;
+ double d = 3.14;
+
+ b = bson_new ();
+ ok (bson_append_double (b, "double", d), "bson_append_double() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 21, "BSON double element size check");
+ ok (memcmp (bson_data (b),
+ "\025\000\000\000\001\144\157\165\142\154\145\000\037\205\353"
+ "\121\270\036\011\100\000",
+ bson_size (b)) == 0,
+ "BSON double element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_double (b, NULL, d) == FALSE,
+ "bson_append_double() with a NULL key should fail");
+ ok (bson_append_double (NULL, "double", d) == FALSE,
+ "bson_append_double() without a BSON object should fail");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_double (b, "d", d) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_double);
diff --git a/tests/unit/bson/bson_append_int32.c b/tests/unit/bson/bson_append_int32.c
new file mode 100644
index 0000000..e2a2867
--- /dev/null
+++ b/tests/unit/bson/bson_append_int32.c
@@ -0,0 +1,40 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_int32 (void)
+{
+ bson *b;
+ gint32 i = 1984;
+
+ b = bson_new ();
+ ok (bson_append_int32 (b, "i32", i), "bson_append_int32() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 14, "BSON int32 element size check");
+ ok (memcmp (bson_data (b),
+ "\016\000\000\000\020\151\063\062\000\300\007\000\000\000",
+ bson_size (b)) == 0,
+ "BSON int32 element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_int32 (b, NULL, i) == FALSE,
+ "bson_append_int32() with a NULL key should fail");
+ ok (bson_append_int32 (NULL, "i32", i) == FALSE,
+ "bson_append_int32() without a BSON object should fail");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_int32 (b, "i32", i) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_int32);
diff --git a/tests/unit/bson/bson_append_int64.c b/tests/unit/bson/bson_append_int64.c
new file mode 100644
index 0000000..22d9691
--- /dev/null
+++ b/tests/unit/bson/bson_append_int64.c
@@ -0,0 +1,41 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_int64 (void)
+{
+ bson *b;
+ gint64 l = 9876543210;
+
+ b = bson_new ();
+ ok (bson_append_int64 (b, "i64", l), "bson_append_int64() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 18, "BSON int64 element size check");
+ ok (memcmp (bson_data (b),
+ "\022\000\000\000\022\151\066\064\000\352\026\260\114\002\000"
+ "\000\000\000",
+ bson_size (b)) == 0,
+ "BSON int64 element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_int64 (b, NULL, l) == FALSE,
+ "bson_append_int64() with a NULL key should fail");
+ ok (bson_append_int64 (NULL, "i64", l) == FALSE,
+ "bson_append_int64() without a BSON object should fail");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_int64 (b, "i64", l) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_int64);
diff --git a/tests/unit/bson/bson_append_js_code.c b/tests/unit/bson/bson_append_js_code.c
new file mode 100644
index 0000000..4b250b0
--- /dev/null
+++ b/tests/unit/bson/bson_append_js_code.c
@@ -0,0 +1,66 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_js_code (void)
+{
+ bson *b;
+
+ /* Test #1: A single JS element, with default size. */
+ b = bson_new ();
+ ok (bson_append_javascript (b, "hello",
+ "function () { print (\"hello world!\"); }", -1),
+ "bson_append_javascript() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 56, "BSON javascript element size check");
+ ok (memcmp (bson_data (b),
+ "\070\000\000\000\015\150\145\154\154\157\000\050\000\000\000"
+ "\146\165\156\143\164\151\157\156\040\050\051\040\173\040\160"
+ "\162\151\156\164\040\050\042\150\145\154\154\157\040\167\157"
+ "\162\154\144\041\042\051\073\040\175\000\000",
+ bson_size (b)) == 0,
+ "BSON javascript element contents check");
+ bson_free (b);
+
+ /* Test #2: A single javascript element, with explicit length. */
+ b = bson_new ();
+ ok (bson_append_javascript (b, "hello",
+ "print (\"hello world!\"); garbage is gone.",
+ strlen ("print (\"hello world!\");")),
+ "bson_append_javascript() with explicit length works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 40, "BSON javascript element size check, #2");
+ ok (memcmp (bson_data (b),
+ "\050\000\000\000\015\150\145\154\154\157\000\030\000\000\000"
+ "\160\162\151\156\164\040\050\042\150\145\154\154\157\040\167"
+ "\157\162\154\144\041\042\051\073\000\000",
+ bson_size (b)) == 0,
+ "BSON javascript element contents check, #2");
+ bson_free (b);
+
+ /* Test #3: Negative test, passing an invalid arguments. */
+ b = bson_new ();
+ ok (bson_append_javascript (b, "hello", "print();", -42) == FALSE,
+ "bson_append_javascript() with an invalid length should fail");
+ ok (bson_append_javascript (b, NULL, "print();", -1) == FALSE,
+ "bson_append_javascript() should fail without a key name");
+ ok (bson_append_javascript (b, "hello", NULL, -1) == FALSE,
+ "bson_append_javascript() should fail without javascript code");
+ ok (bson_append_javascript (NULL, "hello", "print();", -1) == FALSE,
+ "bson_append_javascript() should fail without a BSON object");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_javascript (b, "js", "print();", -1) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (12, bson_js_code);
diff --git a/tests/unit/bson/bson_append_js_code_w_scope.c b/tests/unit/bson/bson_append_js_code_w_scope.c
new file mode 100644
index 0000000..09297f3
--- /dev/null
+++ b/tests/unit/bson/bson_append_js_code_w_scope.c
@@ -0,0 +1,79 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_js_code_w_scope (void)
+{
+ bson *b, *scope;
+
+ scope = bson_new ();
+ bson_append_string (scope, "foo", "bar", -1);
+ bson_finish (scope);
+
+ /* Test #1: A single JS element, with default size. */
+ b = bson_new ();
+ ok (bson_append_javascript_w_scope (b, "f",
+ "alert ('hello');", -1,
+ scope),
+ "bson_append_javascript_w_scope() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 51, "BSON javascript w/ element size check");
+ ok (memcmp (bson_data (b),
+ "\063\000\000\000\017\146\000\053\000\000\000\021\000\000\000"
+ "\141\154\145\162\164\040\050\047\150\145\154\154\157\047\051"
+ "\073\000\022\000\000\000\002\146\157\157\000\004\000\000\000"
+ "\142\141\162\000\000\000",
+ bson_size (b)) == 0,
+ "BSON javascript w/ scope element contents check");
+ bson_free (b);
+
+ /* Test #2: A single javascript element, with explicit length. */
+ b = bson_new ();
+ ok (bson_append_javascript_w_scope (b, "f",
+ "alert ('hello'); garbage",
+ strlen ("alert ('hello');"),
+ scope),
+ "bson_append_javascript_w_scope() with explicit length works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 51, "BSON javascript w/ element size check");
+ ok (memcmp (bson_data (b),
+ "\063\000\000\000\017\146\000\053\000\000\000\021\000\000\000"
+ "\141\154\145\162\164\040\050\047\150\145\154\154\157\047\051"
+ "\073\000\022\000\000\000\002\146\157\157\000\004\000\000\000"
+ "\142\141\162\000\000\000",
+ bson_size (b)) == 0,
+ "BSON javascript w/ scope element contents check");
+ bson_free (b);
+
+ /* Test #3: Negative test, passing an invalid arguments. */
+ b = bson_new ();
+
+ ok (bson_append_javascript_w_scope (b, "hello", "print();",
+ -42, scope) == FALSE,
+ "bson_append_javascript_w_scope() with an invalid length should fail");
+ ok (bson_append_javascript_w_scope (b, NULL, "print();", -1, scope) == FALSE,
+ "bson_append_javascript_w_scope() should fail without a key name");
+ ok (bson_append_javascript_w_scope (b, "hello", NULL, -1, scope) == FALSE,
+ "bson_append_javascript_w_scope() should fail without javascript code");
+ ok (bson_append_javascript_w_scope (NULL, "hello", "print();",
+ -1, scope) == FALSE,
+ "bson_append_javascript_w_scope() should fail without a BSON object");
+ ok (bson_append_javascript_w_scope (b, "hello", "print();",
+ -1, NULL) == FALSE,
+ "bson_append_javascript_w_scope() should fail without a scope object");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_javascript_w_scope (b, "js", "print();", -1, scope) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (13, bson_js_code_w_scope);
diff --git a/tests/unit/bson/bson_append_null.c b/tests/unit/bson/bson_append_null.c
new file mode 100644
index 0000000..294ea50
--- /dev/null
+++ b/tests/unit/bson/bson_append_null.c
@@ -0,0 +1,40 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_null (void)
+{
+ bson *b;
+
+ b = bson_new ();
+ ok (bson_append_null (b, "null"),
+ "bson_append_null() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 11, "BSON NULL element size check");
+ ok (memcmp (bson_data (b),
+ "\013\000\000\000\012\156\165\154\154\000\000",
+ bson_size (b)) == 0,
+ "BSON NULL element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_null (b, NULL) == FALSE,
+ "bson_append_null() should fail without a key name");
+ ok (bson_append_null (NULL, "null") == FALSE,
+ "bson_append_null() should fail without a BSON object");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_null (b, "null") == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_null);
diff --git a/tests/unit/bson/bson_append_oid.c b/tests/unit/bson/bson_append_oid.c
new file mode 100644
index 0000000..2db75a3
--- /dev/null
+++ b/tests/unit/bson/bson_append_oid.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_oid (void)
+{
+ bson *b;
+ guint8 oid[] = "1234567890ab";
+
+ b = bson_new ();
+ ok (bson_append_oid (b, "_id", oid), "bson_append_oid() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 22, "BSON OID element size check");
+ ok (memcmp (bson_data (b),
+ "\026\000\000\000\007\137\151\144\000\061\062\063\064\065\066"
+ "\067\070\071\060\141\142\000",
+ bson_size (b)) == 0,
+ "BSON OID element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_oid (b, "_id", NULL) == FALSE,
+ "bson_append_oid() should fail without an OID");
+ ok (bson_append_oid (b, NULL, oid) == FALSE,
+ "bson_append_oid() should fail without a key name");
+ ok (bson_append_oid (NULL, "_id", oid) == FALSE,
+ "bson_append_oid() should fail without a BSON object");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_oid (b, "_id", oid) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (8, bson_oid);
diff --git a/tests/unit/bson/bson_append_regexp.c b/tests/unit/bson/bson_append_regexp.c
new file mode 100644
index 0000000..172cd2a
--- /dev/null
+++ b/tests/unit/bson/bson_append_regexp.c
@@ -0,0 +1,45 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_regex (void)
+{
+ bson *b;
+
+ b = bson_new ();
+ ok (bson_append_regex (b, "regex", "foo.*bar", "i"),
+ "bson_append_regex() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 23, "BSON regex element size check");
+ ok (memcmp (bson_data (b),
+ "\027\000\000\000\013\162\145\147\145\170\000\146\157\157\056"
+ "\052\142\141\162\000\151\000\000",
+ bson_size (b)) == 0,
+ "BSON regex element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_regex (b, "regex", "foo.*bar", NULL) == FALSE,
+ "bson_append_regex() without options should fail");
+ ok (bson_append_regex (b, "regex", NULL, "i") == FALSE,
+ "bson_append_regex() without a regex should fail");
+ ok (bson_append_regex (b, NULL, "foo.*bar", "i") == FALSE,
+ "bson_append_regex() should fail without a key name");
+ ok (bson_append_regex (NULL, "regex", "foo.*bar", "i") == FALSE,
+ "bson_append_regex() should fail without a BSON object");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_regex (b, "regex", "foo.*bar", "i") == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (9, bson_regex);
diff --git a/tests/unit/bson/bson_append_string.c b/tests/unit/bson/bson_append_string.c
new file mode 100644
index 0000000..e6a73a7
--- /dev/null
+++ b/tests/unit/bson/bson_append_string.c
@@ -0,0 +1,61 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_string (void)
+{
+ bson *b;
+
+ /* Test #1: A single string element, with default size. */
+ b = bson_new ();
+ ok (bson_append_string (b, "hello", "world", -1),
+ "bson_append_string() works");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 22, "BSON string element size check");
+ ok (memcmp (bson_data (b),
+ "\026\000\000\000\002\150\145\154\154\157\000\006\000\000\000"
+ "\167\157\162\154\144\000\000",
+ bson_size (b)) == 0,
+ "BSON string element contents check");
+ bson_free (b);
+
+ /* Test #2: A single string element, with explicit length. */
+ b = bson_new ();
+ ok (bson_append_string (b, "goodbye",
+ "cruel world, this garbage is gone.",
+ strlen ("cruel world")),
+ "bson_append_string() with explicit length works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 30, "BSON string element size check, #2");
+ ok (memcmp (bson_data (b),
+ "\036\000\000\000\002\147\157\157\144\142\171\145\000\014\000"
+ "\000\000\143\162\165\145\154\040\167\157\162\154\144\000\000",
+ bson_size (b)) == 0,
+ "BSON string element contents check, #2");
+ bson_free (b);
+
+ /* Test #3: Negative test, passing invalid arguments. */
+ b = bson_new ();
+ ok (bson_append_string (b, "hello", "world", -42) == FALSE,
+ "bson_append_string() should fail with invalid length");
+ ok (bson_append_string (b, "hello", NULL, -1) == FALSE,
+ "bson_append_string() should fail without a string");
+ ok (bson_append_string (b, NULL, "world", -1) == FALSE,
+ "bson_append_string() should fail without a key name");
+ ok (bson_append_string (NULL, "hello", "world", -1) == FALSE,
+ "bson_append_string() should fail without a BSON object");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_string (b, "hello", "world", -1) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (12, bson_string);
diff --git a/tests/unit/bson/bson_append_symbol.c b/tests/unit/bson/bson_append_symbol.c
new file mode 100644
index 0000000..6c16301
--- /dev/null
+++ b/tests/unit/bson/bson_append_symbol.c
@@ -0,0 +1,61 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_symbol (void)
+{
+ bson *b;
+
+ /* Test #1: A single symbol element, with default size. */
+ b = bson_new ();
+ ok (bson_append_symbol (b, "hello", "world", -1),
+ "bson_append_symbol() works");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 22, "BSON symbol element size check");
+ ok (memcmp (bson_data (b),
+ "\026\000\000\000\016\150\145\154\154\157\000\006\000\000\000"
+ "\167\157\162\154\144\000\000",
+ bson_size (b)) == 0,
+ "BSON symbol element contents check");
+ bson_free (b);
+
+ /* Test #2: A single symbol element, with explicit length. */
+ b = bson_new ();
+ ok (bson_append_symbol (b, "goodbye",
+ "cruel world, this garbage is gone.",
+ strlen ("cruel world")),
+ "bson_append_symbol() with explicit length works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 30, "BSON symbol element size check, #2");
+ ok (memcmp (bson_data (b),
+ "\036\000\000\000\016\147\157\157\144\142\171\145\000\014\000"
+ "\000\000\143\162\165\145\154\040\167\157\162\154\144\000\000",
+ bson_size (b)) == 0,
+ "BSON symbol element contents check, #2");
+ bson_free (b);
+
+ /* Test #3: Negative test, passing invalid arguments. */
+ b = bson_new ();
+ ok (bson_append_symbol (b, "hello", "world", -42) == FALSE,
+ "bson_append_symbol() should fail with invalid length");
+ ok (bson_append_symbol (b, "hello", NULL, -1) == FALSE,
+ "bson_append_symbol() should fail without a string");
+ ok (bson_append_symbol (b, NULL, "world", -1) == FALSE,
+ "bson_append_symbol() should fail without a key name");
+ ok (bson_append_symbol (NULL, "hello", "world", -1) == FALSE,
+ "bson_append_symbol() should fail without a BSON object");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_symbol (b, "hello", "world", -1) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (12, bson_symbol);
diff --git a/tests/unit/bson/bson_append_timestamp.c b/tests/unit/bson/bson_append_timestamp.c
new file mode 100644
index 0000000..4864ef4
--- /dev/null
+++ b/tests/unit/bson/bson_append_timestamp.c
@@ -0,0 +1,41 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_timestamp (void)
+{
+ bson *b;
+ gint64 l = 9876543210;
+
+ b = bson_new ();
+ ok (bson_append_timestamp (b, "ts", l), "bson_append_timestamp() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 17, "BSON timestamp element size check");
+ ok (memcmp (bson_data (b),
+ "\021\000\000\000\021\164\163\000\352\026\260\114\002\000\000"
+ "\000\000",
+ bson_size (b)) == 0,
+ "BSON timestamp element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_timestamp (b, NULL, l) == FALSE,
+ "bson_append_timestamp() with a NULL key should fail");
+ ok (bson_append_timestamp (NULL, "ts", l) == FALSE,
+ "bson_append_timestamp() without a BSON object should fail");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_timestamp (b, "ts", l) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_timestamp);
diff --git a/tests/unit/bson/bson_append_utc_datetime.c b/tests/unit/bson/bson_append_utc_datetime.c
new file mode 100644
index 0000000..b2e38fb
--- /dev/null
+++ b/tests/unit/bson/bson_append_utc_datetime.c
@@ -0,0 +1,41 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_utc_datetime (void)
+{
+ bson *b;
+
+ b = bson_new ();
+ ok (bson_append_utc_datetime (b, "date", 1294860709000),
+ "bson_append_utc_datetime() works");
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 19, "BSON UTC datetime element size check");
+ ok (memcmp (bson_data (b),
+ "\023\000\000\000\011\144\141\164\145\000\210\154\266\173\055"
+ "\001\000\000\000",
+ bson_size (b)) == 0,
+ "BSON UTC datetime element contents check");
+
+ bson_free (b);
+
+ b = bson_new ();
+ ok (bson_append_utc_datetime (b, NULL, 1294860709000) == FALSE,
+ "bson_append_utc_datetime() with a NULL key should fail");
+ ok (bson_append_utc_datetime (NULL, "date", 1294860709000) == FALSE,
+ "bson_append_utc_datetime() without a BSON object should fail");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "BSON object should be empty");
+
+ ok (bson_append_utc_datetime (b, "date", 1294860709000) == FALSE,
+ "Appending to a finished element should fail");
+
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_utc_datetime);
diff --git a/tests/unit/bson/bson_build.c b/tests/unit/bson/bson_build.c
new file mode 100644
index 0000000..29693db
--- /dev/null
+++ b/tests/unit/bson/bson_build.c
@@ -0,0 +1,70 @@
+#include "bson.h"
+#include "tap.h"
+#include "test.h"
+
+#include <string.h>
+#include <glib.h>
+
+void
+test_bson_build (void)
+{
+ bson *b, *o, *d, *a, *scope;
+ guint8 oid[] = "1234567890ab";
+
+ a = bson_build (BSON_TYPE_INT32, "0", 32,
+ BSON_TYPE_INT64, "1", (gint64)-42,
+ BSON_TYPE_NONE);
+ bson_finish (a);
+ d = bson_build (BSON_TYPE_STRING, "name", "sub-document", -1,
+ BSON_TYPE_INT32, "answer", 42,
+ BSON_TYPE_NONE);
+ bson_finish (d);
+
+ scope = bson_build (BSON_TYPE_STRING, "v", "hello world", -1,
+ BSON_TYPE_NONE);
+ bson_finish (scope);
+
+ b = bson_build (BSON_TYPE_DOUBLE, "double", 3.14,
+ BSON_TYPE_STRING, "str", "hello world", -1,
+ BSON_TYPE_DOCUMENT, "doc", d,
+ BSON_TYPE_ARRAY, "array", a,
+ BSON_TYPE_BINARY, "binary0", BSON_BINARY_SUBTYPE_GENERIC,
+ (guint8 *)"foo\0bar", 7,
+ BSON_TYPE_OID, "_id", oid,
+ BSON_TYPE_BOOLEAN, "TRUE", FALSE,
+ BSON_TYPE_UTC_DATETIME, "date", 1294860709000,
+ BSON_TYPE_TIMESTAMP, "ts", 1294860709000,
+ BSON_TYPE_NULL, "null",
+ BSON_TYPE_REGEXP, "foobar", "s/foo.*bar/", "i",
+ BSON_TYPE_JS_CODE, "alert", "alert (\"hello world!\");", -1,
+ BSON_TYPE_SYMBOL, "sex", "Marilyn Monroe", -1,
+ BSON_TYPE_JS_CODE_W_SCOPE, "print", "alert (v);", -1, scope,
+ BSON_TYPE_INT32, "int32", 32,
+ BSON_TYPE_INT64, "int64", (gint64)-42,
+ BSON_TYPE_NONE);
+ bson_finish (b);
+ bson_free (d);
+ bson_free (a);
+ bson_free (scope);
+
+ o = test_bson_generate_full ();
+
+ cmp_ok (bson_size (b), "==", bson_size (o),
+ "bson_build() and hand crafted BSON object sizes match");
+ ok (memcmp (bson_data (b), bson_data (o), bson_size (b)) == 0,
+ "bson_build() and hand crafted BSON objects match");
+
+ bson_free (b);
+ bson_free (o);
+
+ b = bson_build (BSON_TYPE_UNDEFINED, BSON_TYPE_NONE);
+ ok (b == NULL,
+ "bson_build() should fail with an unsupported element type");
+ b = bson_build (BSON_TYPE_STRING, "str", "hello", -1,
+ BSON_TYPE_UNDEFINED,
+ BSON_TYPE_NONE);
+ ok (b == NULL,
+ "bson_build() should fail with an unsupported element type");
+}
+
+RUN_TEST (4, bson_build);
diff --git a/tests/unit/bson/bson_build_full.c b/tests/unit/bson/bson_build_full.c
new file mode 100644
index 0000000..08f2e45
--- /dev/null
+++ b/tests/unit/bson/bson_build_full.c
@@ -0,0 +1,71 @@
+#include "bson.h"
+#include "tap.h"
+#include "test.h"
+
+#include <string.h>
+#include <glib.h>
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+void
+test_bson_build_full (void)
+{
+ bson *b, *o;
+
+ b = bson_build_full (BSON_TYPE_DOUBLE, "double", FALSE, 3.14,
+ BSON_TYPE_STRING, "str", FALSE, "hello world", -1,
+ BSON_TYPE_DOCUMENT, "doc", TRUE,
+ bson_build (BSON_TYPE_STRING, "name", "sub-document", -1,
+ BSON_TYPE_INT32, "answer", 42,
+ BSON_TYPE_NONE),
+ BSON_TYPE_ARRAY, "array", TRUE,
+ bson_build (BSON_TYPE_INT32, "0", 32,
+ BSON_TYPE_INT64, "1", (gint64)-42,
+ BSON_TYPE_NONE),
+ BSON_TYPE_BINARY, "binary0", FALSE, BSON_BINARY_SUBTYPE_GENERIC,
+ "foo\0bar", 7,
+ BSON_TYPE_OID, "_id", FALSE, "1234567890ab",
+ BSON_TYPE_BOOLEAN, "TRUE", FALSE, FALSE,
+ BSON_TYPE_UTC_DATETIME, "date", FALSE, 1294860709000,
+ BSON_TYPE_TIMESTAMP, "ts", FALSE, 1294860709000,
+ BSON_TYPE_NULL, "null", FALSE,
+ BSON_TYPE_REGEXP, "foobar", FALSE, "s/foo.*bar/", "i",
+ BSON_TYPE_JS_CODE, "alert", FALSE, "alert (\"hello world!\");", -1,
+ BSON_TYPE_SYMBOL, "sex", FALSE, "Marilyn Monroe", -1,
+ BSON_TYPE_JS_CODE_W_SCOPE, "print", TRUE, "alert (v);", -1,
+ bson_build (BSON_TYPE_STRING, "v", "hello world", -1,
+ BSON_TYPE_NONE),
+ BSON_TYPE_INT32, "int32", FALSE, 32,
+ BSON_TYPE_INT64, "int64", FALSE, (gint64)-42,
+ BSON_TYPE_NONE);
+ bson_finish (b);
+
+ o = test_bson_generate_full ();
+
+ cmp_ok (bson_size (b), "==", bson_size (o),
+ "bson_build_full() and hand crafted BSON object sizes match");
+
+ ok (memcmp (bson_data (b), bson_data (o), bson_size (b)) == 0,
+ "bson_build_full() and hand crafted BSON objects match");
+
+ bson_free (b);
+ bson_free (o);
+
+ b = bson_build_full (BSON_TYPE_UNDEFINED, "undef", FALSE,
+ BSON_TYPE_NONE);
+ ok (b == NULL,
+ "bson_build_full() should fail with an unsupported element type");
+ b = bson_build_full (BSON_TYPE_STRING, "str", FALSE, "hello", -1,
+ BSON_TYPE_UNDEFINED, "undef", FALSE,
+ BSON_TYPE_NONE);
+ ok (b == NULL,
+ "bson_build_full() should fail with an unsupported element type");
+
+}
+
+RUN_TEST (4, bson_build_full);
diff --git a/tests/unit/bson/bson_cursor_find.c b/tests/unit/bson/bson_cursor_find.c
new file mode 100644
index 0000000..db1afd5
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_find.c
@@ -0,0 +1,39 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_find (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ b = test_bson_generate_full ();
+ c = bson_find (b, "TRUE");
+
+ ok (bson_cursor_find (c, NULL) == FALSE,
+ "bson_cursor_find() should fail with a NULL key");
+ ok (bson_cursor_find (NULL, "int32") == FALSE,
+ "bson_cursor_find() should fail with a NULL cursor");
+
+ ok (bson_cursor_find (c, "sex") == TRUE,
+ "bson_cursor_find() works");
+
+ ok (bson_cursor_find (c, "str") == TRUE,
+ "bson_cursor_find() should wrap over if neccessary");
+
+ ok (bson_cursor_find (c, "-invalid-key-") == FALSE,
+ "bson_cursor_find() should fail when the key is not found");
+
+ ok (bson_cursor_find (c, "int64") == TRUE,
+ "bson_cursor_find() works, even after a previous failure");
+
+ ok (bson_cursor_find (c, "int6") == FALSE,
+ "bson_cursor_find() does not match prefixes");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_find);
diff --git a/tests/unit/bson/bson_cursor_find_next.c b/tests/unit/bson/bson_cursor_find_next.c
new file mode 100644
index 0000000..96d7f0e
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_find_next.c
@@ -0,0 +1,33 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_find_next (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ b = test_bson_generate_full ();
+ c = bson_find (b, "TRUE");
+
+ ok (bson_cursor_find_next (c, NULL) == FALSE,
+ "bson_cursor_find_next() should fail with a NULL key");
+ ok (bson_cursor_find_next (NULL, "int32") == FALSE,
+ "bson_cursor_find_next() should fail with a NULL cursor");
+
+ ok (bson_cursor_find_next (c, "sex") == TRUE,
+ "bson_cursor_find_next() works");
+
+ ok (bson_cursor_find_next (c, "str") == FALSE,
+ "bson_cursor_find_next() should fail when the key is not found");
+
+ ok (bson_cursor_find_next (c, "int64") == TRUE,
+ "bson_cursor_find_next() works, even after a previous failure");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (5, bson_cursor_find_next);
diff --git a/tests/unit/bson/bson_cursor_get_array.c b/tests/unit/bson/bson_cursor_get_array.c
new file mode 100644
index 0000000..becdc5d
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_array.c
@@ -0,0 +1,44 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_array (void)
+{
+ bson *b, *a = NULL;
+ bson_cursor *c;
+
+ ok (bson_cursor_get_array (NULL, &a) == FALSE,
+ "bson_cursor_get_array() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_array (c, NULL) == FALSE,
+ "bson_cursor_get_array() with a NULL destination fails");
+ ok (bson_cursor_get_array (c, &a) == FALSE,
+ "bson_cursor_get_array() at the initial position fails");
+ ok (a == NULL,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "array");
+ ok (bson_cursor_get_array (c, &a),
+ "bson_cursor_get_array() works");
+ cmp_ok (bson_size (a), ">", 0,
+ "the returned document is finished");
+ bson_free (a);
+
+ bson_cursor_next (c);
+
+ ok (bson_cursor_get_array (c, &a) == FALSE,
+ "bson_cursor_get_array() fails if the cursor points to "
+ "non-array data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_array);
diff --git a/tests/unit/bson/bson_cursor_get_binary.c b/tests/unit/bson/bson_cursor_get_binary.c
new file mode 100644
index 0000000..3ec0dc3
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_binary.c
@@ -0,0 +1,60 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_binary (void)
+{
+ bson *b;
+ bson_cursor *c;
+ const guint8 *d = (guint8 *)"deadbeef";
+ bson_binary_subtype t = 0xff;
+ gint32 s = -1;
+
+ ok (bson_cursor_get_binary (NULL, &t, &d, &s) == FALSE,
+ "bson_cursor_get_binary() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_binary (c, NULL, NULL, NULL) == FALSE,
+ "bson_cursor_get_binary() with NULL destinations fails");
+ ok (bson_cursor_get_binary (c, NULL, &d, &s) == FALSE,
+ "bson_cursor_get_binary() with a NULL subtype destination fails");
+ ok (bson_cursor_get_binary (c, &t, NULL, &s) == FALSE,
+ "bson_cursor_get_binary() with a NULL binary destination fails");
+ ok (bson_cursor_get_binary (c, &t, &d, NULL) == FALSE,
+ "bson_cursor_get_binary() with a NULL size destination fails");
+ ok (bson_cursor_get_binary (c, &t, &d, &s) == FALSE,
+ "bson_cursor_get_binary() at the initial position fails");
+ ok (memcmp (d, "deadbeef", sizeof ("deadbeef")) == 0,
+ "binary destination remains unchanged after failed cursor operations");
+ cmp_ok (t, "==", 0xff,
+ "subtype destination remains unchanged after failed cursor "
+ "operations");
+ cmp_ok (s, "==", -1,
+ "size destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "binary0");
+ ok (bson_cursor_get_binary (c, &t, &d, &s),
+ "bson_cursor_get_binary() works");
+ cmp_ok (s, "==", 7,
+ "bson_cursor_get_binary() returns the correct result");
+ ok (memcmp (d, "foo\0bar", s) == 0,
+ "bson_cursor_get_binary() returns the correct result");
+ cmp_ok (t, "==", BSON_BINARY_SUBTYPE_GENERIC,
+ "bson_cursor_get_binary() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_binary (c, &t, &d, &s) == FALSE,
+ "bson_cursor_get_binary() should fail when the cursor points to "
+ "non-binary data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (14, bson_cursor_get_binary);
diff --git a/tests/unit/bson/bson_cursor_get_boolean.c b/tests/unit/bson/bson_cursor_get_boolean.c
new file mode 100644
index 0000000..079d2b2
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_boolean.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_boolean (void)
+{
+ bson *b;
+ bson_cursor *c;
+ gboolean d = TRUE;
+
+ ok (bson_cursor_get_boolean (NULL, &d) == FALSE,
+ "bson_cursor_get_boolean() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_boolean (c, NULL) == FALSE,
+ "bson_cursor_get_boolean() with a NULL destination fails");
+ ok (bson_cursor_get_boolean (c, &d) == FALSE,
+ "bson_cursor_get_boolean() at the initial position fails");
+ cmp_ok (d, "==", TRUE,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "TRUE");
+ ok (bson_cursor_get_boolean (c, &d),
+ "bson_cursor_get_boolean() works");
+ cmp_ok (d, "==", FALSE,
+ "bson_cursor_get_boolean() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_boolean (c, &d) == FALSE,
+ "bson_cursor_get_boolean() should fail when the cursor points to "
+ "non-double data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_boolean);
diff --git a/tests/unit/bson/bson_cursor_get_document.c b/tests/unit/bson/bson_cursor_get_document.c
new file mode 100644
index 0000000..107ecf5
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_document.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_document (void)
+{
+ bson *b, *d = NULL;
+ bson_cursor *c;
+
+ ok (bson_cursor_get_document (NULL, &d) == FALSE,
+ "bson_cursor_get_document() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_document (c, NULL) == FALSE,
+ "bson_cursor_get_document() with a NULL destination fails");
+ ok (bson_cursor_get_document (c, &d) == FALSE,
+ "bson_cursor_get_document() at the initial position fails");
+ ok (d == NULL,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "doc");
+ ok (bson_cursor_get_document (c, &d),
+ "bson_cursor_get_document() works");
+ cmp_ok (bson_size (d), ">", 0,
+ "the returned document is finished");
+ bson_free (d);
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_document (c, &d) == FALSE,
+ "bson_cursor_get_document() fails if the cursor points to "
+ "non-document data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_document);
diff --git a/tests/unit/bson/bson_cursor_get_double.c b/tests/unit/bson/bson_cursor_get_double.c
new file mode 100644
index 0000000..7b5cdff
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_double.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_double (void)
+{
+ bson *b;
+ bson_cursor *c;
+ gdouble d = 12.34;
+
+ ok (bson_cursor_get_double (NULL, &d) == FALSE,
+ "bson_cursor_get_double() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_double (c, NULL) == FALSE,
+ "bson_cursor_get_double() with a NULL destination fails");
+ ok (bson_cursor_get_double (c, &d) == FALSE,
+ "bson_cursor_get_double() at the initial position fails");
+ ok (d == 12.34,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "double");
+ ok (bson_cursor_get_double (c, &d),
+ "bson_cursor_get_double() works");
+ ok (d == 3.14,
+ "bson_cursor_get_double() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_double (c, &d) == FALSE,
+ "bson_cursor_get_double() should fail when the cursor points to "
+ "non-double data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_double);
diff --git a/tests/unit/bson/bson_cursor_get_int32.c b/tests/unit/bson/bson_cursor_get_int32.c
new file mode 100644
index 0000000..caea604
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_int32.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_int32 (void)
+{
+ bson *b;
+ bson_cursor *c;
+ gint d = 12345;
+
+ ok (bson_cursor_get_int32 (NULL, &d) == FALSE,
+ "bson_cursor_get_int32() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_int32 (c, NULL) == FALSE,
+ "bson_cursor_get_int32() with a NULL destination fails");
+ ok (bson_cursor_get_int32 (c, &d) == FALSE,
+ "bson_cursor_get_int32() at the initial position fails");
+ cmp_ok (d, "==", 12345,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "int32");
+ ok (bson_cursor_get_int32 (c, &d),
+ "bson_cursor_get_int32() works");
+ cmp_ok (d, "==", 32,
+ "bson_cursor_get_int32() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_int32 (c, &d) == FALSE,
+ "bson_cursor_get_int32() should fail when the cursor points to "
+ "non-int32 data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_int32);
diff --git a/tests/unit/bson/bson_cursor_get_int64.c b/tests/unit/bson/bson_cursor_get_int64.c
new file mode 100644
index 0000000..d1c80f5
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_int64.c
@@ -0,0 +1,45 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_int64 (void)
+{
+ bson *b;
+ bson_cursor *c;
+ gint64 d = (gint64)987654;
+
+ ok (bson_cursor_get_int64 (NULL, &d) == FALSE,
+ "bson_cursor_get_int64() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_int64 (c, NULL) == FALSE,
+ "bson_cursor_get_int64() with a NULL destination fails");
+ ok (bson_cursor_get_int64 (c, &d) == FALSE,
+ "bson_cursor_get_int64() at the initial position fails");
+ cmp_ok (d, "==", 987654,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "int64");
+ ok (bson_cursor_get_int64 (c, &d),
+ "bson_cursor_get_int64() works");
+ cmp_ok (d, "==", (gint64)-42,
+ "bson_cursor_get_int64() returns the correct result");
+
+ bson_cursor_free (c);
+
+ c = bson_find (b, "double");
+ ok (bson_cursor_get_int64 (c, &d) == FALSE,
+ "bson_cursor_get_int64() should fail when the cursor points to "
+ "non-int64 data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_int64);
diff --git a/tests/unit/bson/bson_cursor_get_javascript.c b/tests/unit/bson/bson_cursor_get_javascript.c
new file mode 100644
index 0000000..4231cbd
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_javascript.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_javascript (void)
+{
+ bson *b;
+ bson_cursor *c;
+ const gchar *s = "deadbeef";
+
+ ok (bson_cursor_get_javascript (NULL, &s) == FALSE,
+ "bson_cursor_get_javascript() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_javascript (c, NULL) == FALSE,
+ "bson_cursor_get_javascript() with a NULL destination fails");
+ ok (bson_cursor_get_javascript (c, &s) == FALSE,
+ "bson_cursor_get_javascript() at the initial position fails");
+ is (s, "deadbeef",
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "alert");
+ ok (bson_cursor_get_javascript (c, &s),
+ "bson_cursor_get_javascript() works");
+ is (s, "alert (\"hello world!\");",
+ "bson_cursor_get_javascript() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_javascript (c, &s) == FALSE,
+ "bson_cursor_get_javascript() should fail when the cursor points to "
+ "non-javascript data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_javascript);
diff --git a/tests/unit/bson/bson_cursor_get_javascript_w_scope.c b/tests/unit/bson/bson_cursor_get_javascript_w_scope.c
new file mode 100644
index 0000000..2e0b9ca
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_javascript_w_scope.c
@@ -0,0 +1,57 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_javascript_w_scope (void)
+{
+ bson *b, *scope = NULL, *valid;
+ bson_cursor *c;
+ const gchar *s = "deadbeef";
+
+ ok (bson_cursor_get_javascript_w_scope (NULL, &s, &scope) == FALSE,
+ "bson_cursor_get_javascript_w_scope() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_javascript_w_scope (c, NULL, &scope) == FALSE,
+ "bson_cursor_get_javascript_w_scope() with a NULL js destination fails");
+ ok (bson_cursor_get_javascript_w_scope (c, &s, NULL) == FALSE,
+ "bson_cursor_get_javascript_w_scope() with a NULL scope destinatin fails");
+ ok (bson_cursor_get_javascript_w_scope (c, &s, &scope) == FALSE,
+ "bson_cursor_get_javascript_w_scope() at the initial position fails");
+ is (s, "deadbeef",
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "print");
+ ok (bson_cursor_get_javascript_w_scope (c, &s, &scope),
+ "bson_cursor_get_javascript_w_scope() works");
+ is (s, "alert (v);",
+ "bson_cursor_get_javascript_w_scope() returns the correct result");
+
+ valid = bson_new ();
+ bson_append_string (valid, "v", "hello world", -1);
+ bson_finish (valid);
+
+ cmp_ok (bson_size (scope), "==", bson_size (valid),
+ "The returned scope's length is correct");
+ ok (memcmp (bson_data (scope), bson_data (valid),
+ bson_size (scope)) == 0,
+ "The returned scope is correct");
+ bson_free (valid);
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_javascript_w_scope (c, &s, &scope) == FALSE,
+ "bson_cursor_get_javascript_w_scope() should fail when the cursor "
+ "points to non-javascript data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+ bson_free (scope);
+}
+
+RUN_TEST (10, bson_cursor_get_javascript_w_scope);
diff --git a/tests/unit/bson/bson_cursor_get_oid.c b/tests/unit/bson/bson_cursor_get_oid.c
new file mode 100644
index 0000000..5c2d77b
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_oid.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_oid (void)
+{
+ bson *b;
+ bson_cursor *c;
+ const gchar *s = "abababababab";
+
+ ok (bson_cursor_get_oid (NULL, (const guint8 **)&s) == FALSE,
+ "bson_cursor_get_oid() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_oid (c, NULL) == FALSE,
+ "bson_cursor_get_oid() with a NULL destination fails");
+ ok (bson_cursor_get_oid (c, (const guint8 **)&s) == FALSE,
+ "bson_cursor_get_oid() at the initial position fails");
+ ok (memcmp (s, "abababababab", 12) == 0,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "_id");
+ ok (bson_cursor_get_oid (c, (const guint8 **)&s),
+ "bson_cursor_get_oid() works");
+ ok (memcmp (s, "1234567890ab", 12) == 0,
+ "bson_cursor_get_oid() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_oid (c, (const guint8 **)&s) == FALSE,
+ "bson_cursor_get_oid() should fail when the cursor points to "
+ "non-oid data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_oid);
diff --git a/tests/unit/bson/bson_cursor_get_regex.c b/tests/unit/bson/bson_cursor_get_regex.c
new file mode 100644
index 0000000..59edefd
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_regex.c
@@ -0,0 +1,52 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_regex (void)
+{
+ bson *b;
+ bson_cursor *c;
+ const gchar *r = "deadbeef";
+ const gchar *o = "g";
+
+ ok (bson_cursor_get_regex (NULL, &r, &o) == FALSE,
+ "bson_cursor_get_regex() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_regex (c, NULL, NULL) == FALSE,
+ "bson_cursor_get_regex() with NULL destinations fails");
+ ok (bson_cursor_get_regex (c, &r, NULL) == FALSE,
+ "bson_cursor_get_regex() with a NULL option destination fails");
+ ok (bson_cursor_get_regex (c, NULL, &o) == FALSE,
+ "bson_cursor_get_regex() with a NULL regex destination fails");
+ ok (bson_cursor_get_regex (c, &r, &o) == FALSE,
+ "bson_cursor_get_regex() at the initial position fails");
+ is (r, "deadbeef",
+ "regex destination remains unchanged after failed cursor operations");
+ is (o, "g",
+ "options destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "foobar");
+ ok (bson_cursor_get_regex (c, &r, &o),
+ "bson_cursor_get_regex() works");
+ is (r, "s/foo.*bar/",
+ "bson_cursor_get_regex() returns the correct result");
+ is (o, "i",
+ "bson_cursor_get_regex() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_regex (c, &r, &o) == FALSE,
+ "bson_cursor_get_regex() should fail when the cursor points to "
+ "non-regex data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (11, bson_cursor_get_regex);
diff --git a/tests/unit/bson/bson_cursor_get_string.c b/tests/unit/bson/bson_cursor_get_string.c
new file mode 100644
index 0000000..18662bb
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_string.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_string (void)
+{
+ bson *b;
+ bson_cursor *c;
+ const gchar *s = "deadbeef";
+
+ ok (bson_cursor_get_string (NULL, &s) == FALSE,
+ "bson_cursor_get_string() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_string (c, NULL) == FALSE,
+ "bson_cursor_get_string() with a NULL destination fails");
+ ok (bson_cursor_get_string (c, &s) == FALSE,
+ "bson_cursor_get_string() at the initial position fails");
+ is (s, "deadbeef",
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "str");
+ ok (bson_cursor_get_string (c, &s),
+ "bson_cursor_get_string() works");
+ is (s, "hello world",
+ "bson_cursor_get_string() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_string (c, &s) == FALSE,
+ "bson_cursor_get_string() should fail when the cursor points to "
+ "non-string data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_string);
diff --git a/tests/unit/bson/bson_cursor_get_symbol.c b/tests/unit/bson/bson_cursor_get_symbol.c
new file mode 100644
index 0000000..785e71b
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_symbol.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_symbol (void)
+{
+ bson *b;
+ bson_cursor *c;
+ const gchar *s = "deadbeef";
+
+ ok (bson_cursor_get_symbol (NULL, &s) == FALSE,
+ "bson_cursor_get_symbol() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_symbol (c, NULL) == FALSE,
+ "bson_cursor_get_symbol() with a NULL destination fails");
+ ok (bson_cursor_get_symbol (c, &s) == FALSE,
+ "bson_cursor_get_symbol() at the initial position fails");
+ is (s, "deadbeef",
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "sex");
+ ok (bson_cursor_get_symbol (c, &s),
+ "bson_cursor_get_symbol() works");
+ is (s, "Marilyn Monroe",
+ "bson_cursor_get_symbol() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_symbol (c, &s) == FALSE,
+ "bson_cursor_get_symbol() should fail when the cursor points to "
+ "non-symbol data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_symbol);
diff --git a/tests/unit/bson/bson_cursor_get_timestamp.c b/tests/unit/bson/bson_cursor_get_timestamp.c
new file mode 100644
index 0000000..3bfc86c
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_timestamp.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_timestamp (void)
+{
+ bson *b;
+ bson_cursor *c;
+ gint64 d = (gint64)987654;
+
+ ok (bson_cursor_get_timestamp (NULL, &d) == FALSE,
+ "bson_cursor_get_timestamp() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_timestamp (c, NULL) == FALSE,
+ "bson_cursor_get_timestamp() with a NULL destination fails");
+ ok (bson_cursor_get_timestamp (c, &d) == FALSE,
+ "bson_cursor_get_timestamp() at the initial position fails");
+ cmp_ok (d, "==", 987654,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "ts");
+ ok (bson_cursor_get_timestamp (c, &d),
+ "bson_cursor_get_timestamp() works");
+ ok (d == 1294860709000,
+ "bson_cursor_get_timestamp() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_timestamp (c, &d) == FALSE,
+ "bson_cursor_get_timestamp() should fail when the cursor points to "
+ "non-timestamp data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_timestamp);
diff --git a/tests/unit/bson/bson_cursor_get_utc_datetime.c b/tests/unit/bson/bson_cursor_get_utc_datetime.c
new file mode 100644
index 0000000..70e1332
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_get_utc_datetime.c
@@ -0,0 +1,43 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_get_utc_datetime (void)
+{
+ bson *b;
+ bson_cursor *c;
+ gint64 d = (gint64)987654;
+
+ ok (bson_cursor_get_utc_datetime (NULL, &d) == FALSE,
+ "bson_cursor_get_utc_datetime() with a NULL cursor fails");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_get_utc_datetime (c, NULL) == FALSE,
+ "bson_cursor_get_utc_datetime() with a NULL destination fails");
+ ok (bson_cursor_get_utc_datetime (c, &d) == FALSE,
+ "bson_cursor_get_utc_datetime() at the initial position fails");
+ cmp_ok (d, "==", 987654,
+ "destination remains unchanged after failed cursor operations");
+ bson_cursor_free (c);
+
+ c = bson_find (b, "date");
+ ok (bson_cursor_get_utc_datetime (c, &d),
+ "bson_cursor_get_utc_datetime() works");
+ ok (d == 1294860709000,
+ "bson_cursor_get_utc_datetime() returns the correct result");
+
+ bson_cursor_next (c);
+ ok (bson_cursor_get_utc_datetime (c, &d) == FALSE,
+ "bson_cursor_get_utc_datetime() should fail when the cursor points to "
+ "non-datetime data");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (7, bson_cursor_get_utc_datetime);
diff --git a/tests/unit/bson/bson_cursor_key.c b/tests/unit/bson/bson_cursor_key.c
new file mode 100644
index 0000000..7db98b6
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_key.c
@@ -0,0 +1,30 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_key (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ is (bson_cursor_key (NULL), NULL,
+ "bson_cursor_key(NULL) should fail");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ is (bson_cursor_key (c), NULL,
+ "bson_cursor_key() should fail at the initial position");
+ bson_cursor_next (c);
+
+ is (bson_cursor_key (c), "double",
+ "bson_cursor_key() works");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (3, bson_cursor_key);
diff --git a/tests/unit/bson/bson_cursor_new.c b/tests/unit/bson/bson_cursor_new.c
new file mode 100644
index 0000000..7bcb32b
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_new.c
@@ -0,0 +1,28 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_new (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ ok (bson_cursor_new (NULL) == NULL,
+ "bson_cursor_new(NULL) should fail");
+
+ b = bson_new ();
+ ok (bson_cursor_new (b) == NULL,
+ "bson_cursor_new() should fail with an unfinished BSON object");
+ bson_free (b);
+
+ b = test_bson_generate_full ();
+ ok ((c = bson_cursor_new (b)) != NULL,
+ "bson_cursor_new() works");
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (3, bson_cursor_new);
diff --git a/tests/unit/bson/bson_cursor_next.c b/tests/unit/bson/bson_cursor_next.c
new file mode 100644
index 0000000..a2fc137
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_next.c
@@ -0,0 +1,42 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_next (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ ok (bson_cursor_next (NULL) == FALSE,
+ "bson_cursor_next (NULL) should fail");
+
+ b = bson_new ();
+ bson_finish (b);
+ c = bson_cursor_new (b);
+
+ ok (bson_cursor_next (c) == FALSE,
+ "bson_cursor_next() should fail with an empty document");
+
+ bson_cursor_free (c);
+ bson_free (b);
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+ ok (bson_cursor_next (c),
+ "initial bson_cursor_next() works");
+ ok (bson_cursor_next (c),
+ "subsequent bson_cursor_next() works too");
+
+ while (bson_cursor_next (c)) ;
+
+ ok (bson_cursor_next (c) == FALSE,
+ "bson_cursor_next() fails after the end of the BSON object");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (5, bson_cursor_next);
diff --git a/tests/unit/bson/bson_cursor_type.c b/tests/unit/bson/bson_cursor_type.c
new file mode 100644
index 0000000..86be005
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_type.c
@@ -0,0 +1,30 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_type (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ cmp_ok (bson_cursor_type (NULL), "==", BSON_TYPE_NONE,
+ "bson_cursor_type(NULL) should fail");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_NONE,
+ "bson_cursor_type() should fail at the beginning of the BSON "
+ "object");
+ bson_cursor_next (c);
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_DOUBLE,
+ "bson_cursor_type() works");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (3, bson_cursor_type);
diff --git a/tests/unit/bson/bson_cursor_type_as_string.c b/tests/unit/bson/bson_cursor_type_as_string.c
new file mode 100644
index 0000000..8ee6fea
--- /dev/null
+++ b/tests/unit/bson/bson_cursor_type_as_string.c
@@ -0,0 +1,31 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_cursor_type_as_string (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ is (bson_cursor_type_as_string (NULL), NULL,
+ "bson_cursor_type_as_string(NULL) should fail");
+
+ b = test_bson_generate_full ();
+ c = bson_cursor_new (b);
+
+ is (bson_cursor_type_as_string (c), NULL,
+ "bson_cursor_type_as_string() should fail at the initial position");
+ bson_cursor_next (c);
+
+ is (bson_cursor_type_as_string (c),
+ bson_type_as_string (bson_cursor_type (c)),
+ "bson_cursor_type_as_string() works");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (3, bson_cursor_type_as_string);
diff --git a/tests/unit/bson/bson_empty.c b/tests/unit/bson/bson_empty.c
new file mode 100644
index 0000000..69d840a
--- /dev/null
+++ b/tests/unit/bson/bson_empty.c
@@ -0,0 +1,22 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_empty (void)
+{
+ bson *b;
+
+ b = bson_new ();
+ bson_finish (b);
+
+ cmp_ok (bson_size (b), "==", 5, "Empty BSON size check");
+ ok (memcmp (bson_data (b), "\005\000\000\000\000", bson_size (b)) == 0,
+ "Empty BSON contents check");
+
+ bson_free (b);
+}
+
+RUN_TEST (2, bson_empty)
diff --git a/tests/unit/bson/bson_find.c b/tests/unit/bson/bson_find.c
new file mode 100644
index 0000000..54e8767
--- /dev/null
+++ b/tests/unit/bson/bson_find.c
@@ -0,0 +1,34 @@
+#include "tap.h"
+#include "test.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_bson_find (void)
+{
+ bson *b;
+ bson_cursor *c;
+
+ ok (bson_find (NULL, NULL) == NULL,
+ "bson_find() with NULL parameters should fail");
+ ok (bson_find (NULL, "key") == NULL,
+ "bson_find() with a NULL BSON object should fail");
+ b = bson_new ();
+ ok (bson_find (b, "key") == NULL,
+ "bson_find() with an unfinished BSON object should fail");
+ bson_free (b);
+
+ b = test_bson_generate_full ();
+ ok (bson_find (b, NULL) == FALSE,
+ "bson_find() with a NULL key should fail");
+ ok (bson_find (b, "__invalid__") == FALSE,
+ "bson_find() with a non-existent key should fail");
+ ok ((c = bson_find (b, "alert")) != NULL,
+ "bson_find() works");
+
+ bson_cursor_free (c);
+ bson_free (b);
+}
+
+RUN_TEST (6, bson_find);
diff --git a/tests/unit/bson/bson_new.c b/tests/unit/bson/bson_new.c
new file mode 100644
index 0000000..3149027
--- /dev/null
+++ b/tests/unit/bson/bson_new.c
@@ -0,0 +1,28 @@
+#include "bson.h"
+#include "test.h"
+#include "tap.h"
+
+#include <string.h>
+
+void
+test_bson_new (void)
+{
+ bson *b;
+
+ ok ((b = bson_new ()) != NULL, "bson_new() works");
+ ok (bson_data (b) == NULL,
+ "bson_data() with an unfished object should fail");
+ ok (bson_size (b) == -1,
+ "bson_size() with an unfinished object should fail");
+ ok (bson_finish (b), "bson_finish() works");
+ ok (bson_finish (b),
+ "bson_finish() works on an already finished object too");
+ bson_free (b);
+
+ ok (bson_size (NULL) == -1, "bson_size(NULL) works correctly");
+ ok (bson_data (NULL) == NULL, "bson_data(NULL) works correctly");
+ ok (bson_finish (NULL) == FALSE, "bson_finish(NULL) works correctly");
+ bson_free (NULL);
+}
+
+RUN_TEST (8, bson_new);
diff --git a/tests/unit/bson/bson_new_from_data.c b/tests/unit/bson/bson_new_from_data.c
new file mode 100644
index 0000000..740cb6e
--- /dev/null
+++ b/tests/unit/bson/bson_new_from_data.c
@@ -0,0 +1,46 @@
+#include "bson.h"
+#include "test.h"
+#include "tap.h"
+
+#include <string.h>
+
+void
+test_bson_new_from_data (void)
+{
+ bson *orig, *new;
+
+ orig = test_bson_generate_full ();
+
+ ok (bson_new_from_data (NULL, 0) == NULL,
+ "bson_new_from_data (NULL, 0) fails");
+ ok (bson_new_from_data (NULL, bson_size (orig)) == NULL,
+ "bson_new_from_data (NULL, size) fails");
+ ok (bson_new_from_data (bson_data (orig), 0) == NULL,
+ "bson_new_from_data (orig, 0) fails");
+ ok (bson_new_from_data (bson_data (orig), -1) == NULL,
+ "bson_new_from_data (orig, -1) fails");
+ ok (bson_new_from_data (NULL, -1) == NULL,
+ "bson_new_from_data (NULL, -1) fails");
+
+ ok ((new = bson_new_from_data (bson_data (orig),
+ bson_size (orig) - 1)) != NULL,
+ "bson_new_from_data() works");
+ cmp_ok (bson_size (new), "==", -1,
+ "Copied object is unfinished");
+ bson_finish (new);
+
+ ok (orig != new, "Copied BSON object is not the same as the original");
+
+ cmp_ok (bson_size (orig), "==", bson_size (new),
+ "Copied (& finished) object has the same size as the original");
+ ok (bson_data (orig) != bson_data (new),
+ "The copied data is not the same as the original");
+ ok (memcmp (bson_data (orig), bson_data (new),
+ bson_size (orig)) == 0,
+ "The copied data is identical to the original");
+
+ bson_free (orig);
+ bson_free (new);
+}
+
+RUN_TEST (11, bson_new_from_data);
diff --git a/tests/unit/bson/bson_reset.c b/tests/unit/bson/bson_reset.c
new file mode 100644
index 0000000..23f2ce6
--- /dev/null
+++ b/tests/unit/bson/bson_reset.c
@@ -0,0 +1,27 @@
+#include "bson.h"
+#include "test.h"
+#include "tap.h"
+
+void
+test_bson_reset (void)
+{
+ bson *b;
+
+ b = test_bson_generate_full ();
+
+ cmp_ok (bson_size (b), "!=", -1,
+ "bson_size() != -1 on a non-empty document");
+ ok (bson_reset (b), "bson_reset() works");
+ cmp_ok (bson_size (b), "==", -1,
+ "bson_size() on a reseted object returns an error");
+ bson_finish (b);
+ cmp_ok (bson_size (b), "==", 5,
+ "bson_size() on a reseted & finished object matches the "
+ "size of an empty document");
+ bson_free (b);
+
+ ok (bson_reset (NULL) == FALSE,
+ "bson_reset(NULL) should fail");
+}
+
+RUN_TEST (5, bson_reset);
diff --git a/tests/unit/bson/bson_type_as_string.c b/tests/unit/bson/bson_type_as_string.c
new file mode 100644
index 0000000..35e8210
--- /dev/null
+++ b/tests/unit/bson/bson_type_as_string.c
@@ -0,0 +1,40 @@
+#include "bson.h"
+#include "test.h"
+#include "tap.h"
+
+#include <string.h>
+
+#define CHECK_TYPE(t) \
+ is (bson_type_as_string (t), #t, \
+ "bson_type_as_string(%s) works", #t)
+
+void
+test_bson_type_as_string (void)
+{
+ CHECK_TYPE (BSON_TYPE_NONE);
+ CHECK_TYPE (BSON_TYPE_DOUBLE);
+ CHECK_TYPE (BSON_TYPE_STRING);
+ CHECK_TYPE (BSON_TYPE_DOCUMENT);
+ CHECK_TYPE (BSON_TYPE_ARRAY);
+ CHECK_TYPE (BSON_TYPE_BINARY);
+ CHECK_TYPE (BSON_TYPE_UNDEFINED);
+ CHECK_TYPE (BSON_TYPE_OID);
+ CHECK_TYPE (BSON_TYPE_BOOLEAN);
+ CHECK_TYPE (BSON_TYPE_UTC_DATETIME);
+ CHECK_TYPE (BSON_TYPE_NULL);
+ CHECK_TYPE (BSON_TYPE_REGEXP);
+ CHECK_TYPE (BSON_TYPE_DBPOINTER);
+ CHECK_TYPE (BSON_TYPE_JS_CODE);
+ CHECK_TYPE (BSON_TYPE_SYMBOL);
+ CHECK_TYPE (BSON_TYPE_JS_CODE_W_SCOPE);
+ CHECK_TYPE (BSON_TYPE_INT32);
+ CHECK_TYPE (BSON_TYPE_TIMESTAMP);
+ CHECK_TYPE (BSON_TYPE_INT64);
+ CHECK_TYPE (BSON_TYPE_MIN);
+ CHECK_TYPE (BSON_TYPE_MAX);
+
+ ok (bson_type_as_string (42) == NULL,
+ "bson_type_as_string() returns NULL on invalid type.");
+}
+
+RUN_TEST (22, bson_type_as_string);
diff --git a/tests/unit/bson/bson_validate_key.c b/tests/unit/bson/bson_validate_key.c
new file mode 100644
index 0000000..126b1fd
--- /dev/null
+++ b/tests/unit/bson/bson_validate_key.c
@@ -0,0 +1,36 @@
+#include "tap.h"
+#include "test.h"
+
+#include <errno.h>
+#include <bson.h>
+#include <string.h>
+
+void
+test_bson_validate_key (void)
+{
+ gboolean valid;
+
+ valid = bson_validate_key (NULL, FALSE, FALSE);
+ ok (valid == FALSE && errno == EINVAL,
+ "bson_validate_key() sets errno when the key is NULL");
+
+ valid = bson_validate_key ("$foo.bar", FALSE, FALSE);
+ ok (valid == TRUE,
+ "bson_validate_key() returns success if both checks are off");
+
+ valid = bson_validate_key ("$foo.bar", FALSE, TRUE);
+ ok (valid == FALSE,
+ "bson_validate_key() returns failiure if the key starts with a $");
+ valid = bson_validate_key ("foo.bar$", FALSE, TRUE);
+ ok (valid == TRUE,
+ "bson_validate_key() returns success if the key does not start with a $");
+
+ valid = bson_validate_key ("foo.bar", TRUE, TRUE);
+ ok (valid == FALSE,
+ "bson_validate_key() returns failiure if the key contains a dot");
+ valid = bson_validate_key ("foobar", TRUE, TRUE);
+ ok (valid == TRUE,
+ "bson_validate_key() returns success if the key does not contain a dot");
+}
+
+RUN_TEST (6, bson_validate_key)
diff --git a/tests/unit/mongo/client/connect.c b/tests/unit/mongo/client/connect.c
new file mode 100644
index 0000000..fc390ea
--- /dev/null
+++ b/tests/unit/mongo/client/connect.c
@@ -0,0 +1,34 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-client.h"
+
+#include <errno.h>
+
+void
+test_mongo_connect (void)
+{
+ mongo_connection *c;
+
+ ok (mongo_connect (NULL, 27010) == NULL,
+ "mongo_connect() fails with a NULL host");
+ ok (errno == EINVAL,
+ "mongo_connect() should fail with EINVAL if host is NULL");
+
+ begin_network_tests (4);
+
+ ok (mongo_connect ("invalid.example.com", 27017) == NULL,
+ "Connecting to an invalid host fails");
+ ok (mongo_connect ("example.com", 27017) == NULL,
+ "Connecting to an unavailable host/port fails");
+ ok (mongo_connect ("/does/not/exist.sock", MONGO_CONN_LOCAL) == NULL,
+ "Connecting to an unavailable unix socket fails");
+
+ ok ((c = mongo_connect (config.primary_host,
+ config.primary_port)) != NULL,
+ "Connecting to the primary server works");
+ mongo_disconnect (c);
+
+ end_network_tests ();
+}
+
+RUN_TEST (6, mongo_connect);
diff --git a/tests/unit/mongo/client/connection_get_requestid.c b/tests/unit/mongo/client/connection_get_requestid.c
new file mode 100644
index 0000000..9232689
--- /dev/null
+++ b/tests/unit/mongo/client/connection_get_requestid.c
@@ -0,0 +1,44 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_connection_get_requestid (void)
+{
+ mongo_connection c, *conn;
+ mongo_packet *p;
+ bson *b;
+ gint reqid;
+
+ c.request_id = 42;
+
+ ok (mongo_connection_get_requestid (NULL) == -1,
+ "mongo_connection_get_requestid() fails with a NULL connection");
+ ok (mongo_connection_get_requestid (&c) == 42,
+ "mongo_connection_get_requestid() works");
+
+ begin_network_tests (2);
+
+ b = bson_new ();
+ bson_append_int32 (b, "getnonce", 1);
+ bson_finish (b);
+
+ p = mongo_wire_cmd_custom (42, config.db, 0, b);
+ bson_free (b);
+
+ conn = mongo_connect (config.primary_host, config.primary_port);
+ cmp_ok ((reqid = mongo_connection_get_requestid (conn)), "==", 0,
+ "Initial request id is 0");
+ mongo_packet_send (conn, p);
+ mongo_wire_packet_free (p);
+
+ cmp_ok (reqid, "<", mongo_connection_get_requestid (conn),
+ "Old request ID is smaller than the new one");
+
+ mongo_disconnect (conn);
+
+ end_network_tests ();
+}
+
+RUN_TEST (4, mongo_connection_get_requestid);
diff --git a/tests/unit/mongo/client/connection_set_timeout.c b/tests/unit/mongo/client/connection_set_timeout.c
new file mode 100644
index 0000000..02468bf
--- /dev/null
+++ b/tests/unit/mongo/client/connection_set_timeout.c
@@ -0,0 +1,33 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_connection_set_timeout (void)
+{
+ mongo_connection c, *conn;
+
+ c.fd = -1;
+
+ ok (mongo_connection_set_timeout (NULL, 100) == FALSE,
+ "mongo_connection_set_timeout() should fail with a NULL connection");
+ ok (mongo_connection_set_timeout (&c, -1) == FALSE,
+ "mongo_connection_set_timeout() should fail with a negative timeout");
+ ok (mongo_connection_set_timeout (&c, 100) == FALSE,
+ "mongo_connection_set_timeout() should fail with an invalid FD");
+
+ begin_network_tests (0);
+
+ conn = mongo_connect (config.primary_host, config.primary_port);
+
+ /* No verification here, as some systems may or may not support
+ this, thus, failing in a test is not fatal. */
+ mongo_connection_set_timeout (conn, 100);
+
+ mongo_disconnect (conn);
+
+ end_network_tests ();
+}
+
+RUN_TEST (3, mongo_connection_set_timeout);
diff --git a/tests/unit/mongo/client/disconnect.c b/tests/unit/mongo/client/disconnect.c
new file mode 100644
index 0000000..1b0be93
--- /dev/null
+++ b/tests/unit/mongo/client/disconnect.c
@@ -0,0 +1,32 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-client.h"
+
+#include "libmongo-private.h"
+#include <errno.h>
+
+void
+test_mongo_disconnect (void)
+{
+ mongo_connection *conn;
+
+ conn = g_new0 (mongo_connection, 1);
+ conn->fd = -1;
+
+ errno = 0;
+ mongo_disconnect (NULL);
+ ok (errno == ENOTCONN,
+ "mongo_disconnect() fails with ENOTCONN when passed a NULL connection");
+
+ mongo_disconnect (conn);
+ ok (errno == 0,
+ "mongo_disconnect() works");
+
+ conn = g_new0 (mongo_connection, 1);
+ conn->fd = 100;
+ mongo_disconnect (conn);
+ ok (errno == 0,
+ "mongo_disconnect() works, even with a bogus FD");
+}
+
+RUN_TEST (3, mongo_disconnect);
diff --git a/tests/unit/mongo/client/packet_recv.c b/tests/unit/mongo/client/packet_recv.c
new file mode 100644
index 0000000..51ccb3d
--- /dev/null
+++ b/tests/unit/mongo/client/packet_recv.c
@@ -0,0 +1,56 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "libmongo-private.h"
+
+void
+test_mongo_packet_recv (void)
+{
+ mongo_connection c, *conn;
+ mongo_packet *p;
+ bson *b;
+
+ c.fd = -1;
+
+ ok (mongo_packet_recv (NULL) == NULL,
+ "mongo_packet_recv() fails with a NULL connection");
+ ok (errno == ENOTCONN,
+ "mongo_packet_recv() sets errno to ENOTCONN if connection is NULL");
+
+ ok (mongo_packet_recv (&c) == NULL,
+ "mongo_packet_recv() fails if the FD is less than zero");
+ ok (errno == EBADF,
+ "mongo_packet_recv() sets errno to EBADF is the FD is bad");
+
+ begin_network_tests (2);
+
+ b = bson_new ();
+ bson_append_int32 (b, "getnonce", 1);
+ bson_finish (b);
+
+ p = mongo_wire_cmd_custom (42, config.db, 0, b);
+ bson_free (b);
+
+ conn = mongo_connect (config.primary_host, config.primary_port);
+ mongo_packet_send (conn, p);
+ mongo_wire_packet_free (p);
+
+ ok ((p = mongo_packet_recv (conn)) != NULL,
+ "mongo_packet_recv() works");
+ mongo_wire_packet_free (p);
+
+ close (conn->fd);
+ sleep (3);
+
+ ok (mongo_packet_recv (conn) == NULL,
+ "mongo_packet_recv() fails on a closed socket");
+
+ mongo_disconnect (conn);
+
+ end_network_tests ();
+}
+
+RUN_TEST (6, mongo_packet_recv);
diff --git a/tests/unit/mongo/client/packet_send.c b/tests/unit/mongo/client/packet_send.c
new file mode 100644
index 0000000..e501a3c
--- /dev/null
+++ b/tests/unit/mongo/client/packet_send.c
@@ -0,0 +1,75 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+#include "mongo-client.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "libmongo-private.h"
+
+void
+test_mongo_packet_send (void)
+{
+ mongo_packet *p;
+ mongo_connection c, *conn;
+ mongo_packet_header h;
+ bson *b;
+
+ p = mongo_wire_cmd_kill_cursors (1, 2, (gint64)3, (gint64)4);
+ c.fd = -1;
+
+ ok (mongo_packet_send (NULL, p) == FALSE,
+ "mongo_packet_send() fails with a NULL connection");
+ ok (errno == ENOTCONN,
+ "mongo_packet_send() with a NULL connection sets errno to ENOTCONN");
+ ok (mongo_packet_send (&c, NULL) == FALSE,
+ "mongo_packet_send() fails with a NULL packet");
+ ok (errno == EINVAL,
+ "mongo_packet_send() with a NULL packet sets errno to EINVAL");
+ ok (mongo_packet_send (&c, p) == FALSE,
+ "mongo_packet_send() fails if the FD is less than zero");
+ ok (errno == EBADF,
+ "mongo_packet_send() sets errno to EBADF is the FD is bad");
+ mongo_wire_packet_free (p);
+
+ p = mongo_wire_packet_new ();
+
+ h.id = 42;
+ h.resp_to = 0;
+ h.opcode = 1;
+ h.length = sizeof (mongo_packet_header);
+ mongo_wire_packet_set_header (p, &h);
+
+ c.fd = 1;
+ ok (mongo_packet_send (&c, p) == FALSE,
+ "mongo_packet_send() fails with an unfinished packet");
+
+ mongo_wire_packet_free (p);
+
+ begin_network_tests (2);
+
+ b = bson_new ();
+ bson_append_int32 (b, "getnonce", 1);
+ bson_finish (b);
+
+ p = mongo_wire_cmd_custom (42, config.db, 0, b);
+ bson_free (b);
+
+ conn = mongo_connect (config.primary_host, config.primary_port);
+ ok (mongo_packet_send (conn, p),
+ "mongo_packet_send() works");
+
+ close (conn->fd);
+ sleep (3);
+
+ ok (mongo_packet_send (conn, p) == FALSE,
+ "mongo_packet_send() fails on a closed socket");
+ mongo_wire_packet_free (p);
+
+ mongo_disconnect (conn);
+
+ end_network_tests ();
+}
+
+RUN_TEST (9, mongo_packet_send);
diff --git a/tests/unit/mongo/sync-cursor/sync_cursor_free.c b/tests/unit/mongo/sync-cursor/sync_cursor_free.c
new file mode 100644
index 0000000..bd01cb5
--- /dev/null
+++ b/tests/unit/mongo/sync-cursor/sync_cursor_free.c
@@ -0,0 +1,34 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include "libmongo-private.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_cursor_free (void)
+{
+ mongo_sync_connection *conn;
+ mongo_packet *p;
+ mongo_sync_cursor *c;
+
+ test_env_setup ();
+
+ p = mongo_wire_packet_new ();
+ conn = test_make_fake_sync_conn (-1, FALSE);
+
+ c = mongo_sync_cursor_new (conn, config.ns, p);
+
+ errno = 0;
+ mongo_sync_cursor_free (NULL);
+ ok (errno == ENOTCONN,
+ "mongo_sync_cursor_free(NULL) sets errno to ENOTCONN");
+ mongo_sync_cursor_free (c);
+ pass ("mongo_sync_cursor_free() works");
+
+ mongo_sync_disconnect (conn);
+ test_env_free ();
+}
+
+RUN_TEST (2, mongo_sync_cursor_free);
diff --git a/tests/unit/mongo/sync-cursor/sync_cursor_get_data.c b/tests/unit/mongo/sync-cursor/sync_cursor_get_data.c
new file mode 100644
index 0000000..0dd391c
--- /dev/null
+++ b/tests/unit/mongo/sync-cursor/sync_cursor_get_data.c
@@ -0,0 +1,51 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include "libmongo-private.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_cursor_get_data (void)
+{
+ mongo_sync_connection *conn;
+ mongo_packet *p;
+ bson *b;
+ mongo_sync_cursor *c;
+
+ test_env_setup ();
+
+ p = test_mongo_wire_generate_reply (TRUE, 4, TRUE);
+ conn = test_make_fake_sync_conn (-1, FALSE);
+
+ c = mongo_sync_cursor_new (conn, config.ns, p);
+
+ errno = 0;
+ b = mongo_sync_cursor_get_data (NULL);
+ ok (b == NULL && errno == EINVAL,
+ "mongo_sync_cursor_get_data(NULL) should fail");
+
+ b = mongo_sync_cursor_get_data (c);
+ ok (b == NULL,
+ "mongo_sync_cursor_get_data() should fail without _cursor_next()");
+
+ mongo_sync_cursor_next (c);
+ b = mongo_sync_cursor_get_data (c);
+ ok (b != NULL,
+ "mongo_sync_cursor_get_data() works");
+
+ c->offset = 5;
+
+ errno = 0;
+ b = mongo_sync_cursor_get_data (c);
+ ok (b == NULL && errno == ERANGE,
+ "mongo_sync_cursor_get_data() should fail if the cursor is "
+ "out of range");
+
+ mongo_sync_cursor_free (c);
+ mongo_sync_disconnect (conn);
+ test_env_free ();
+}
+
+RUN_TEST (4, mongo_sync_cursor_get_data);
diff --git a/tests/unit/mongo/sync-cursor/sync_cursor_new.c b/tests/unit/mongo/sync-cursor/sync_cursor_new.c
new file mode 100644
index 0000000..642d826
--- /dev/null
+++ b/tests/unit/mongo/sync-cursor/sync_cursor_new.c
@@ -0,0 +1,40 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include "libmongo-private.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_cursor_new (void)
+{
+ mongo_sync_connection *conn;
+ mongo_packet *p;
+ mongo_sync_cursor *c;
+
+ test_env_setup ();
+
+ p = mongo_wire_packet_new ();
+ conn = test_make_fake_sync_conn (-1, FALSE);
+
+ c = mongo_sync_cursor_new (conn, config.ns, NULL);
+ ok (c == NULL,
+ "mongo_sync_cursor_new() fails with a NULL packet");
+ c = mongo_sync_cursor_new (conn, NULL, p);
+ ok (c == NULL,
+ "mongo_sync_cursor_new() fails with a NULL namespace");
+ c = mongo_sync_cursor_new (NULL, config.ns, p);
+ ok (c == NULL,
+ "mongo_sync_cursor_new() fails with a NULL connection");
+
+ c = mongo_sync_cursor_new (conn, config.ns, p);
+ ok (c != NULL,
+ "mongo_sync_cursor_new() works");
+
+ mongo_sync_cursor_free (c);
+ mongo_sync_disconnect (conn);
+ test_env_free ();
+}
+
+RUN_TEST (4, mongo_sync_cursor_new);
diff --git a/tests/unit/mongo/sync-cursor/sync_cursor_next.c b/tests/unit/mongo/sync-cursor/sync_cursor_next.c
new file mode 100644
index 0000000..442df96
--- /dev/null
+++ b/tests/unit/mongo/sync-cursor/sync_cursor_next.c
@@ -0,0 +1,40 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include "libmongo-private.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_cursor_next (void)
+{
+ mongo_sync_connection *conn;
+ mongo_packet *p;
+ mongo_sync_cursor *c;
+ gboolean r = TRUE;
+ gint i;
+
+ test_env_setup ();
+
+ p = test_mongo_wire_generate_reply (TRUE, 2, TRUE);
+ conn = test_make_fake_sync_conn (-1, FALSE);
+
+ c = mongo_sync_cursor_new (conn, config.ns, p);
+
+ ok (mongo_sync_cursor_next (NULL) == FALSE,
+ "mongo_sync_cursor_next() should fail with a NULL cursor");
+ for (i = 0; i < 2; i++)
+ r &= mongo_sync_cursor_next (c);
+
+ ok (r == TRUE,
+ "mongo_sync_cursor_next() works");
+ ok (mongo_sync_cursor_next (c) == FALSE,
+ "mongo_sync_cursor_next() should fail past the end of the resultset");
+
+ mongo_sync_cursor_free (c);
+ mongo_sync_disconnect (conn);
+ test_env_free ();
+}
+
+RUN_TEST (3, mongo_sync_cursor_next);
diff --git a/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_get_chunk.c b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_get_chunk.c
new file mode 100644
index 0000000..f16378a
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_get_chunk.c
@@ -0,0 +1,15 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_gridfs_chunked_file_cursor_get_chunk (void)
+{
+ gint32 size;
+
+ ok (mongo_sync_gridfs_chunked_file_cursor_get_chunk (NULL, &size) == NULL,
+ "mongo_sync_gridfs_file_cursor_get_chunk() fails with a NULL cursor");
+}
+
+RUN_TEST (1, mongo_sync_gridfs_chunked_file_cursor_get_chunk);
diff --git a/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_new.c b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_new.c
new file mode 100644
index 0000000..22210f8
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_cursor_new.c
@@ -0,0 +1,19 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_gridfs_chunked_file_cursor_new (void)
+{
+ mongo_sync_gridfs_chunked_file f;
+
+ ok (mongo_sync_gridfs_chunked_file_cursor_new (NULL, 0, 0) == NULL,
+ "mongo_sync_gridfs_file_cursor_new() fails with a NULL file");
+ ok (mongo_sync_gridfs_chunked_file_cursor_new (&f, -1, 0) == NULL,
+ "mongo_sync_gridfs_file_cursor_new() fails with an invalid start position");
+ ok (mongo_sync_gridfs_chunked_file_cursor_new (&f, 0, -1) == NULL,
+ "mongo_sync_gridfs_file_cursor_new() fails with an invalid max number");
+}
+
+RUN_TEST (3, mongo_sync_gridfs_chunked_file_cursor_new);
diff --git a/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_free.c b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_free.c
new file mode 100644
index 0000000..c9fddfa
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_free.c
@@ -0,0 +1,16 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_gridfs_chunked_file_free (void)
+{
+ errno = 0;
+ mongo_sync_gridfs_chunked_file_free (NULL);
+
+ cmp_ok (errno, "==", ENOTCONN,
+ "mongo_sync_gridfs_chunked_file_free() fails with a NULL file");
+}
+
+RUN_TEST (1, mongo_sync_gridfs_chunked_file_free);
diff --git a/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_new_from_buffer.c b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_new_from_buffer.c
new file mode 100644
index 0000000..ba3fa2e
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_file_new_from_buffer.c
@@ -0,0 +1,71 @@
+#include "test.h"
+#include "mongo.h"
+
+#define BUFFER_SIZE 256 * 1024 + 42
+
+void
+test_mongo_sync_gridfs_chunked_file_new_from_buffer (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ bson *metadata;
+ guint8 *buffer;
+ mongo_sync_gridfs_chunked_file *gfile;
+
+ buffer = g_malloc (BUFFER_SIZE);
+ memset (buffer, 'a', BUFFER_SIZE);
+
+ conn = test_make_fake_sync_conn (4, TRUE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ metadata = bson_build (BSON_TYPE_STRING, "filename",
+ "gridfs_file_new_from_buffer", -1,
+ BSON_TYPE_NONE);
+ bson_finish (metadata);
+
+ ok (mongo_sync_gridfs_chunked_file_new_from_buffer (NULL, metadata,
+ buffer, BUFFER_SIZE) == FALSE,
+ "mongo_sync_gridfs_chunked_file_new_from_buffer() fails with a NULL GridFS");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ begin_network_tests (5);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ ok (mongo_sync_gridfs_chunked_file_new_from_buffer (gfs, metadata,
+ NULL, BUFFER_SIZE) == FALSE,
+ "mongo_sync_gridfs_chunked_file_new_from_buffer() fails with NULL data");
+
+ ok (mongo_sync_gridfs_chunked_file_new_from_buffer (gfs, metadata,
+ buffer, 0) == FALSE,
+ "mongo_sync_gridfs_chunked_file_new_from_buffer() fails with an invalid data size");
+
+ ok (mongo_sync_gridfs_chunked_file_new_from_buffer (gfs, metadata, buffer,
+ BUFFER_SIZE) == FALSE,
+ "mongo_sync_gridfs_chunked_file_new_from_buffer() fails with uninitialized OID");
+
+ mongo_util_oid_init (0);
+
+ gfile = mongo_sync_gridfs_chunked_file_new_from_buffer (gfs, metadata,
+ buffer, BUFFER_SIZE);
+ ok (gfile != NULL,
+ "mongo_sync_gridfs_chunked_file_new_from_buffer() works with metadata");
+ mongo_sync_gridfs_chunked_file_free (gfile);
+
+ gfile = mongo_sync_gridfs_chunked_file_new_from_buffer (gfs, NULL,
+ buffer, BUFFER_SIZE);
+ ok (gfile != NULL,
+ "mongo_sync_gridfs_chunked_file_new_from_buffer() works without metadata");
+ mongo_sync_gridfs_chunked_file_free (gfile);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+
+ bson_free (metadata);
+ g_free (buffer);
+}
+
+RUN_TEST (6, mongo_sync_gridfs_chunked_file_new_from_buffer);
diff --git a/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_find.c b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_find.c
new file mode 100644
index 0000000..91514f9
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-chunk/sync_gridfs_chunked_find.c
@@ -0,0 +1,38 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_gridfs_chunked_find (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ bson *query;
+
+ query = bson_build (BSON_TYPE_STRING, "filename", "bogus-fn", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ ok (mongo_sync_gridfs_chunked_find (NULL, query) == NULL,
+ "mongo_sync_gridfs_chunked_find() fails with a NULL GridFS");
+
+ begin_network_tests (2);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ ok (mongo_sync_gridfs_chunked_find (gfs, NULL) == NULL,
+ "mongo_sync_gridfs_chunked_find() fails with a NULL query");
+
+ ok (mongo_sync_gridfs_chunked_find (gfs, query) == NULL,
+ "mongo_sync_gridfs_chunked_find() fails when the file is not found");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+
+ bson_free (query);
+}
+
+RUN_TEST (3, mongo_sync_gridfs_chunked_find);
diff --git a/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_close.c b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_close.c
new file mode 100644
index 0000000..3c8a7b3
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_close.c
@@ -0,0 +1,41 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_gridfs_stream_close (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+
+ mongo_util_oid_init (0);
+
+ ok (mongo_sync_gridfs_stream_close (NULL) == FALSE,
+ "mongo_sync_gridfs_stream_close() fails with a NULL stream");
+
+ begin_network_tests (3);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, NULL);
+ ok (mongo_sync_gridfs_stream_close (stream) == TRUE,
+ "mongo_sync_gridfs_stream_close() works with a write stream");
+
+ stream = mongo_sync_gridfs_stream_new (gfs, NULL);
+ stream->file.type = LMC_GRIDFS_FILE_CHUNKED;
+ ok (mongo_sync_gridfs_stream_close (stream) == FALSE,
+ "mongo_sync_gridfs_stream_close() should fail with a chunked file");
+
+ stream->file.type = LMC_GRIDFS_FILE_STREAM_READER;
+ ok (mongo_sync_gridfs_stream_close (stream) == TRUE,
+ "mongo_sync_gridfs_stream_close() works with a read stream");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+}
+
+RUN_TEST (4, mongo_sync_gridfs_stream_close);
diff --git a/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_find.c b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_find.c
new file mode 100644
index 0000000..643a8b2
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_find.c
@@ -0,0 +1,36 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_gridfs_stream_find (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ bson *query;
+
+ query = bson_build (BSON_TYPE_STRING, "filename", "bogus-fn", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ ok (mongo_sync_gridfs_stream_find (NULL, query) == NULL,
+ "mongo_sync_gridfs_stream_find() should fail with a NULL connection");
+
+ begin_network_tests (2);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ ok (mongo_sync_gridfs_stream_find (gfs, NULL) == NULL,
+ "mongo_sync_gridfs_stream_find() fails with a NULL query");
+
+ ok (mongo_sync_gridfs_stream_find (gfs, query) == NULL,
+ "mongo_sync_gridfs_stream_find() fails if the file is not found");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+
+ bson_free (query);
+}
+
+RUN_TEST (3, mongo_sync_gridfs_stream_find);
diff --git a/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_new.c b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_new.c
new file mode 100644
index 0000000..75e4419
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_new.c
@@ -0,0 +1,43 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_gridfs_stream_new (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ bson *meta;
+
+ mongo_util_oid_init (0);
+
+ meta = bson_build (BSON_TYPE_STRING, "my-id", "sync_gridfs_stream_new", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ ok (mongo_sync_gridfs_stream_new (NULL, meta) == FALSE,
+ "mongo_sync_gridfs_stream_new() should fail with a NULL connection");
+
+ begin_network_tests (2);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, NULL);
+ ok (stream != NULL,
+ "mongo_sync_gridfs_stream_new() works with NULL metadata");
+ mongo_sync_gridfs_stream_close (stream);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, meta);
+ ok (stream != NULL,
+ "mongo_sync_gridfs_stream_new() works with metadata");
+ mongo_sync_gridfs_stream_close (stream);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+
+ bson_free (meta);
+}
+
+RUN_TEST (3, mongo_sync_gridfs_stream_new);
diff --git a/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_read.c b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_read.c
new file mode 100644
index 0000000..a53aa88
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_read.c
@@ -0,0 +1,44 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_gridfs_stream_read (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ guint8 buffer[4096];
+
+ mongo_util_oid_init (0);
+
+ ok (mongo_sync_gridfs_stream_read (NULL, buffer, sizeof (buffer)) == -1,
+ "mongo_sync_gridfs_stream_read() should fail with a NULL connection");
+
+ begin_network_tests (3);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, NULL);
+
+ ok (mongo_sync_gridfs_stream_read (stream, buffer, sizeof (buffer)) == -1,
+ "mongo-sync_gridfs_stream_read() should fail when the stream is "
+ "write-only");
+
+ stream->file.type = LMC_GRIDFS_FILE_STREAM_READER;
+
+ ok (mongo_sync_gridfs_stream_read (stream, NULL, sizeof (buffer)) == -1,
+ "mongo_sync_gridfs_stream_read() should fail with a NULL buffer");
+ ok (mongo_sync_gridfs_stream_read (stream, buffer, 0) == -1,
+ "mongo_sync_gridfs_stream_read() should fail with a 0 size");
+
+ mongo_sync_gridfs_stream_close (stream);
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+}
+
+RUN_TEST (4, mongo_sync_gridfs_stream_read);
diff --git a/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_seek.c b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_seek.c
new file mode 100644
index 0000000..49547bc
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_seek.c
@@ -0,0 +1,65 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+#include <unistd.h>
+
+void
+test_mongo_sync_gridfs_stream_seek (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+
+ mongo_util_oid_init (0);
+
+ ok (mongo_sync_gridfs_stream_seek (NULL, 0, SEEK_SET) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with a NULL stream");
+
+ begin_network_tests (8);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, NULL);
+
+ ok (mongo_sync_gridfs_stream_seek (stream, 0, SEEK_SET) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with a write stream");
+
+ stream->file.type = LMC_GRIDFS_FILE_STREAM_READER;
+
+ ok (mongo_sync_gridfs_stream_seek (stream, -1, SEEK_SET) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with SEEK_SET and a negative "
+ "position");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, 10, SEEK_SET) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with SEEK_SET and a position "
+ "past EOF");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, -1, SEEK_CUR) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with SEEK_CUR and a position "
+ "before the start");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, 10, SEEK_CUR) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with SEEK_CUR and a position "
+ "past EOF");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, 1, SEEK_END) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with SEEK_END and a position "
+ "past EOF");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, -1, SEEK_END) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with SEEK_END and a position "
+ "before the start");
+
+ ok (mongo_sync_gridfs_stream_seek (stream, 0, 42) == FALSE,
+ "mongo_sync_gridfs_stream_seek() fails with an invalid whence");
+
+ mongo_sync_gridfs_stream_close (stream);
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+}
+
+RUN_TEST (9, mongo_sync_gridfs_stream_seek);
diff --git a/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_write.c b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_write.c
new file mode 100644
index 0000000..562c7b4
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs-stream/sync_gridfs_stream_write.c
@@ -0,0 +1,50 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_gridfs_stream_write (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ mongo_sync_gridfs_stream *stream;
+ bson *meta;
+ guint8 buffer[4096];
+
+ mongo_util_oid_init (0);
+
+ meta = bson_build (BSON_TYPE_STRING, "my-id", "sync_gridfs_stream_write", -1,
+ BSON_TYPE_NONE);
+ bson_finish (meta);
+
+ ok (mongo_sync_gridfs_stream_write (NULL, buffer, sizeof (buffer)) == FALSE,
+ "mongo_sync_gridfs_stream_write() should fail with a NULL connection");
+
+ begin_network_tests (4);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ stream = mongo_sync_gridfs_stream_new (gfs, meta);
+
+ ok (mongo_sync_gridfs_stream_write (stream, NULL, sizeof (buffer)) == FALSE,
+ "mongo_sync_gridfs_stream_write() should fail with a NULL buffer");
+ ok (mongo_sync_gridfs_stream_write (stream, buffer, 0) == FALSE,
+ "mongo_sync_gridfs_stream_write() should fail with 0 size");
+ ok (mongo_sync_gridfs_stream_write (stream, buffer, sizeof (buffer)) == TRUE,
+ "mongo_sync_gridfs_stream_write() works");
+
+ stream->file.type = LMC_GRIDFS_FILE_STREAM_READER;
+ ok (mongo_sync_gridfs_stream_write (stream, buffer, sizeof (buffer)) == FALSE,
+ "mongo_sync_gridfs_stream_write() should fail with a read stream");
+
+ mongo_sync_gridfs_stream_close (stream);
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+
+ bson_free (meta);
+}
+
+RUN_TEST (5, mongo_sync_gridfs_stream_write);
diff --git a/tests/unit/mongo/sync-gridfs/sync_gridfs_file_get_metadata.c b/tests/unit/mongo/sync-gridfs/sync_gridfs_file_get_metadata.c
new file mode 100644
index 0000000..2be34e5
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs/sync_gridfs_file_get_metadata.c
@@ -0,0 +1,23 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_gridfs_file_get_metadata (void)
+{
+ ok (mongo_sync_gridfs_file_get_id (NULL) == NULL,
+ "mongo_sync_gridfs_file_get_id() fails with a NULL file");
+ ok (mongo_sync_gridfs_file_get_length (NULL) == -1,
+ "mongo_sync_gridfs_file_get_length() fails with a NULL file");
+ ok (mongo_sync_gridfs_file_get_chunk_size (NULL) == -1,
+ "mongo_sync_gridfs_file_get_chunk_size() fails with a NULL file");
+ ok (mongo_sync_gridfs_file_get_md5 (NULL) == NULL,
+ "mongo_sync_gridfs_file_get_md5() fails with a NULL file");
+ ok (mongo_sync_gridfs_file_get_date (NULL) == -1,
+ "mongo_sync_gridfs_file_get_date() fails with a NULL file");
+ ok (mongo_sync_gridfs_file_get_metadata (NULL) == NULL,
+ "mongo_sync_gridfs_file_get_metadata() fails with a NULL file");
+ ok (mongo_sync_gridfs_file_get_chunks (NULL) == -1,
+ "mongo_sync_gridfs_file_get_chunks() fails with a NULL file");
+}
+
+RUN_TEST (7, mongo_sync_gridfs_file_get_metadata);
diff --git a/tests/unit/mongo/sync-gridfs/sync_gridfs_free.c b/tests/unit/mongo/sync-gridfs/sync_gridfs_free.c
new file mode 100644
index 0000000..1c8c2d6
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs/sync_gridfs_free.c
@@ -0,0 +1,35 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_gridfs_free (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+
+ errno = 0;
+ mongo_sync_gridfs_free (NULL, FALSE);
+ cmp_ok (errno, "==", ENOTCONN,
+ "mongo_sync_gridfs_free() with a NULL connection shall set errno");
+
+ begin_network_tests (2);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+
+ mongo_sync_gridfs_free (gfs, FALSE);
+ cmp_ok (errno, "==", 0,
+ "mongo_sync_gridfs_free() should clear errno on success");
+
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ mongo_sync_gridfs_free (gfs, TRUE);
+ cmp_ok (errno, "==", 0,
+ "mongo_sync_gridfs_free() works when asked to free the "
+ "connection too");
+
+ end_network_tests ();
+}
+
+RUN_TEST (3, mongo_sync_gridfs_free);
diff --git a/tests/unit/mongo/sync-gridfs/sync_gridfs_get_set_chunk_size.c b/tests/unit/mongo/sync-gridfs/sync_gridfs_get_set_chunk_size.c
new file mode 100644
index 0000000..5d17986
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs/sync_gridfs_get_set_chunk_size.c
@@ -0,0 +1,33 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_gridfs_get_set_chunk_size (void)
+{
+ mongo_sync_gridfs *gfs;
+
+ ok (mongo_sync_gridfs_get_chunk_size (NULL) == -1,
+ "mongo_sync_gridfs_get_chunk_size() fails with a NULL gfs");
+ ok (mongo_sync_gridfs_set_chunk_size (NULL, 16 * 1024) == FALSE,
+ "mongo_sync_gridfs_set_chunk_size() fails with a NULL gfs");
+
+ begin_network_tests (3);
+
+ gfs = mongo_sync_gridfs_new (mongo_sync_connect (config.primary_host,
+ config.primary_port,
+ FALSE),
+ config.gfs_prefix);
+
+ ok (mongo_sync_gridfs_set_chunk_size (gfs, -1) == FALSE,
+ "mongo_sync_gridfs_set_chunk_size() fails if the size is invalid");
+ ok (mongo_sync_gridfs_set_chunk_size (gfs, 12345),
+ "mongo_sync_gridfs_set_chunk_size() works");
+ cmp_ok (mongo_sync_gridfs_get_chunk_size (gfs), "==", 12345,
+ "mongo_sync_gridfs_get_chunk_size() works");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+}
+
+RUN_TEST (5, mongo_sync_gridfs_get_set_chunk_size);
diff --git a/tests/unit/mongo/sync-gridfs/sync_gridfs_list.c b/tests/unit/mongo/sync-gridfs/sync_gridfs_list.c
new file mode 100644
index 0000000..e5857ea
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs/sync_gridfs_list.c
@@ -0,0 +1,34 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_gridfs_list (void)
+{
+ mongo_sync_gridfs *gfs;
+ bson *query;
+
+ query = bson_build (BSON_TYPE_STRING, "bogus-key", "bogus-value", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ ok (mongo_sync_gridfs_list (NULL, NULL) == NULL,
+ "mongo_sync_gridfs_list() fails with a NULL GridFS");
+
+ begin_network_tests (1);
+
+ gfs = mongo_sync_gridfs_new
+ (mongo_sync_connect (config.primary_host, config.primary_port, FALSE),
+ config.gfs_prefix);
+
+ ok (mongo_sync_gridfs_list (gfs, query) == NULL,
+ "mongo_sync_gridfs_list() fails with a query that does not match "
+ "anything");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+
+ bson_free (query);
+}
+
+RUN_TEST (2, mongo_sync_gridfs_list);
diff --git a/tests/unit/mongo/sync-gridfs/sync_gridfs_new.c b/tests/unit/mongo/sync-gridfs/sync_gridfs_new.c
new file mode 100644
index 0000000..20d6fea
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs/sync_gridfs_new.c
@@ -0,0 +1,54 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_gridfs_new (void)
+{
+ mongo_sync_connection *conn;
+ mongo_sync_gridfs *gfs;
+ gchar *f, *c;
+
+ conn = test_make_fake_sync_conn (4, TRUE);
+
+ ok (mongo_sync_gridfs_new (NULL, "test.fs") == NULL,
+ "mongo_sync_gridfs_new() should fail with a NULL connection");
+
+ ok (mongo_sync_gridfs_new (conn, "test.fs") == NULL,
+ "mongo_sync_gridfs_new() should fail with a bogus connection");
+
+ ok (mongo_sync_gridfs_new (conn, NULL) == NULL,
+ "mongo_sync_gridfs_new() should fail with a NULL ns prefix");
+
+ ok (mongo_sync_gridfs_new (conn, "bogus") == NULL,
+ "mongo_sync_gridfs_new() should fail with a bogus ns prefix");
+
+ mongo_sync_disconnect (conn);
+
+ begin_network_tests (4);
+
+ f = g_strconcat (config.gfs_prefix, ".files", NULL);
+ c = g_strconcat (config.gfs_prefix, ".chunks", NULL);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+
+ gfs = mongo_sync_gridfs_new (conn, config.gfs_prefix);
+ ok (gfs != NULL,
+ "mongo_sync_gridfs_new() works");
+ is (gfs->ns.prefix, config.gfs_prefix,
+ "The namespace prefix is as specified");
+ is (gfs->ns.files, f,
+ "The files namespace is correct");
+ is (gfs->ns.chunks, c,
+ "The chunks namespace is correct");
+ mongo_sync_gridfs_free (gfs, FALSE);
+
+ mongo_sync_disconnect (conn);
+
+ g_free (f);
+ g_free (c);
+ end_network_tests ();
+}
+
+RUN_TEST (8, mongo_sync_gridfs_new);
diff --git a/tests/unit/mongo/sync-gridfs/sync_gridfs_remove.c b/tests/unit/mongo/sync-gridfs/sync_gridfs_remove.c
new file mode 100644
index 0000000..88eb40b
--- /dev/null
+++ b/tests/unit/mongo/sync-gridfs/sync_gridfs_remove.c
@@ -0,0 +1,34 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_gridfs_remove (void)
+{
+ mongo_sync_gridfs *gfs;
+ bson *query;
+
+ query = bson_build (BSON_TYPE_STRING, "bogus-key", "bogus-value", -1,
+ BSON_TYPE_NONE);
+ bson_finish (query);
+
+ ok (mongo_sync_gridfs_remove (NULL, NULL) == FALSE,
+ "mongo_sync_gridfs_remove() fails with a NULL GridFS");
+
+ begin_network_tests (1);
+
+ gfs = mongo_sync_gridfs_new
+ (mongo_sync_connect (config.primary_host, config.primary_port, FALSE),
+ config.gfs_prefix);
+
+ ok (mongo_sync_gridfs_remove (gfs, query) == FALSE,
+ "mongo_sync_gridfs_remove() fails with a query that does not match "
+ "anything");
+
+ mongo_sync_gridfs_free (gfs, TRUE);
+
+ end_network_tests ();
+
+ bson_free (query);
+}
+
+RUN_TEST (2, mongo_sync_gridfs_remove);
diff --git a/tests/unit/mongo/sync-pool/sync_pool_free.c b/tests/unit/mongo/sync-pool/sync_pool_free.c
new file mode 100644
index 0000000..5f64621
--- /dev/null
+++ b/tests/unit/mongo/sync-pool/sync_pool_free.c
@@ -0,0 +1,11 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_pool_free (void)
+{
+ mongo_sync_pool_free (NULL);
+ pass ("mongo_sync_pool_free(NULL) works");
+}
+
+RUN_TEST (1, mongo_sync_pool_free);
diff --git a/tests/unit/mongo/sync-pool/sync_pool_new.c b/tests/unit/mongo/sync-pool/sync_pool_new.c
new file mode 100644
index 0000000..b9758d2
--- /dev/null
+++ b/tests/unit/mongo/sync-pool/sync_pool_new.c
@@ -0,0 +1,19 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_pool_new (void)
+{
+ ok (mongo_sync_pool_new ("example.com", 27017, 0, 0) == NULL,
+ "mongo_sync_pool_new() needs at least one connection");
+ ok (mongo_sync_pool_new (NULL, 27017, 1, 0) == NULL,
+ "mongo_sync_pool_new() should fail without a HOST");
+ ok (mongo_sync_pool_new ("example.com", -1, 1, 0) == NULL,
+ "mongo_sync_pool_new() should fail with an invalid port");
+ ok (mongo_sync_pool_new ("example.com", 27017, -1, 0) == NULL,
+ "mongo_sync_pool_new() should fail with an invalid number of masters");
+ ok (mongo_sync_pool_new ("example.com", 27017, 10, -1) == NULL,
+ "mongo_sync_pool_new() should fail with an invalid number of slaves");
+}
+
+RUN_TEST (5, mongo_sync_pool_new);
diff --git a/tests/unit/mongo/sync-pool/sync_pool_pick.c b/tests/unit/mongo/sync-pool/sync_pool_pick.c
new file mode 100644
index 0000000..352ba04
--- /dev/null
+++ b/tests/unit/mongo/sync-pool/sync_pool_pick.c
@@ -0,0 +1,11 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_pool_pick (void)
+{
+ ok (mongo_sync_pool_pick (NULL, TRUE) == NULL,
+ "mongo_sync_pool_pick() should fail without a pool");
+}
+
+RUN_TEST (1, mongo_sync_pool_pick);
diff --git a/tests/unit/mongo/sync-pool/sync_pool_return.c b/tests/unit/mongo/sync-pool/sync_pool_return.c
new file mode 100644
index 0000000..d622ede
--- /dev/null
+++ b/tests/unit/mongo/sync-pool/sync_pool_return.c
@@ -0,0 +1,22 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <string.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_pool_return (void)
+{
+ mongo_sync_pool_connection c;
+ void *pool;
+
+ pool = g_malloc (1024);
+
+ ok (mongo_sync_pool_return (NULL, &c) == FALSE,
+ "mongo_sync_pool_return() should fail without a pool");
+ ok (mongo_sync_pool_return ((mongo_sync_pool *)pool, NULL) == FALSE,
+ "mongo_sync_pool_return() should fail without a connection");
+ g_free (pool);
+}
+
+RUN_TEST (2, mongo_sync_pool_return);
diff --git a/tests/unit/mongo/sync/sync_cmd_authenticate.c b/tests/unit/mongo/sync/sync_cmd_authenticate.c
new file mode 100644
index 0000000..a5c67cb
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_authenticate.c
@@ -0,0 +1,112 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_authenticate_net_secondary (void)
+{
+ mongo_sync_connection *c;
+
+ skip (!config.secondary_host, 4,
+ "Secondary server not configured");
+
+ c = mongo_sync_connect (config.secondary_host, config.secondary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+ mongo_sync_cmd_is_master (c);
+
+ ok (mongo_sync_cmd_authenticate (c, config.db, "test", "s3kr1+") == TRUE,
+ "mongo_sync_cmd_authenticate() works");
+ ok (mongo_sync_cmd_authenticate (c, config.db, "test", "bad_pw") == FALSE,
+ "mongo_sync_cmd_authenticate() should fail with a bad password");
+ ok (mongo_sync_cmd_authenticate (c, config.db, "xxx", "s3kr1+") == FALSE,
+ "mongo_sync_cmd_authenticate() should fail with a bad username");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_authenticate (c, config.db, "test", "s3kr1+") == TRUE,
+ "mongo_sync_cmd_authenticate() automatically reconnects");
+
+ mongo_sync_disconnect (c);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_authenticate_net (void)
+{
+ mongo_sync_connection *c;
+
+ begin_network_tests (8);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ mongo_sync_cmd_user_add (c, config.db, "test", "s3kr1+");
+
+ ok (mongo_sync_cmd_authenticate (c, config.db, "test", "s3kr1+") == TRUE,
+ "mongo_sync_cmd_authenticate() works");
+ ok (mongo_sync_cmd_authenticate (c, config.db, "test", "bad_pw") == FALSE,
+ "mongo_sync_cmd_authenticate() should fail with a bad password");
+ ok (mongo_sync_cmd_authenticate (c, config.db, "xxx", "s3kr1+") == FALSE,
+ "mongo_sync_cmd_authenticate() should fail with a bad username");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_authenticate (c, config.db, "test", "s3kr1+") == TRUE,
+ "mongo_sync_cmd_authenticate() automatically reconnects");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_authenticate_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_authenticate (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_authenticate (NULL, "test", "test",
+ "s3kr1+") == FALSE,
+ "mongo_sync_cmd_authenticate() fails with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ errno = 0;
+ ok (mongo_sync_cmd_authenticate (c, NULL, "test", "s3kr1+") == FALSE,
+ "mongo_sync_cmd_authenticate() fails with a NULL db");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ errno = 0;
+ ok (mongo_sync_cmd_authenticate (c, "test", NULL, "s3kr1+") == FALSE,
+ "mongo_sync_cmd_authenticate() fails with a NULL user");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ errno = 0;
+ ok (mongo_sync_cmd_authenticate (c, "test", "test", NULL) == FALSE,
+ "mongo_sync_cmd_authenticate() fails with a NULL password");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ ok (mongo_sync_cmd_authenticate (c, "test", "test",
+ "s3kr1+") == FALSE,
+ "mongo_sync_cmd_authenticate() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_authenticate_net ();
+}
+
+RUN_TEST (17, mongo_sync_cmd_authenticate);
diff --git a/tests/unit/mongo/sync/sync_cmd_authenticate_cache.c b/tests/unit/mongo/sync/sync_cmd_authenticate_cache.c
new file mode 100644
index 0000000..c0581b0
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_authenticate_cache.c
@@ -0,0 +1,60 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_authenticate_cache (void)
+{
+ mongo_sync_conn_recovery_cache *cache;
+ mongo_sync_connection *c;
+
+ begin_network_tests (8);
+
+ cache = mongo_sync_conn_recovery_cache_new ();
+ mongo_sync_conn_recovery_cache_seed_add (cache,
+ config.primary_host,
+ config.primary_port);
+
+ c = mongo_sync_connect_recovery_cache (cache, TRUE);
+
+ mongo_sync_cmd_user_add (c, config.db, "test", "s3kr1+");
+
+ ok (mongo_sync_cmd_authenticate (c, config.db, "test", "s3kr1+") == TRUE,
+ "mongo_sync_cmd_authenticate() works");
+
+ mongo_sync_disconnect (c);
+
+ ok ((cache->auth.db != NULL) && (strcmp (cache->auth.db, config.db) == 0),
+ "db is cached");
+
+ ok ((cache->auth.user != NULL) && (strcmp (cache->auth.user, "test") == 0),
+ "user is cached");
+
+ ok ((cache->auth.pw != NULL) && (strcmp (cache->auth.pw, "s3kr1+") == 0),
+ "pw is cached");
+
+ c = mongo_sync_connect_recovery_cache (cache, TRUE);
+
+ ok (c->auth.db != NULL, "db is loaded from cache");
+
+ ok (c->auth.user != NULL, "username is loaded from cache");
+
+ ok (c->auth.pw != NULL, "password is loaded from cache");
+
+ ok (mongo_sync_cmd_authenticate (c,
+ c->auth.db,
+ c->auth.user,
+ c->auth.pw) == TRUE,
+ "mongo_sync_cmd_authenticate() works with cached auth. credentials");
+
+ mongo_sync_disconnect (c);
+ mongo_sync_conn_recovery_cache_free (cache);
+
+ end_network_tests ();
+}
+
+RUN_TEST (8, mongo_sync_cmd_authenticate_cache);
diff --git a/tests/unit/mongo/sync/sync_cmd_count.c b/tests/unit/mongo/sync/sync_cmd_count.c
new file mode 100644
index 0000000..2cb8645
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_count.c
@@ -0,0 +1,119 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_count_net_secondary (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+ gdouble d;
+
+ skip (!config.secondary_host, 2,
+ "Secondary server not configured");
+
+ conn = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ mongo_sync_cmd_is_master (conn);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ b = bson_new ();
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_finish (b);
+
+ d = mongo_sync_cmd_count (conn, config.db, config.coll, b);
+ ok (d > 0,
+ "mongo_sync_cmd_count() works on the secondary too");
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ d = mongo_sync_cmd_count (conn, config.db, config.coll, b);
+ ok (d > 0,
+ "mongo_sync_cmd_count() automatically reconnects");
+
+ bson_free (b);
+ mongo_sync_disconnect (conn);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_count_net (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+ gdouble d;
+ gint i;
+
+ begin_network_tests (4);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ b = bson_new ();
+ for (i = 0; i < 40; i++)
+ {
+ bson_reset (b);
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_append_int32 (b, "seq", i);
+ bson_finish (b);
+
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+ }
+ bson_free (b);
+
+ b = bson_new ();
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_finish (b);
+
+ d = mongo_sync_cmd_count (conn, config.db, config.coll, b);
+ ok (d > 0,
+ "mongo_sync_cmd_count() works");
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ d = mongo_sync_cmd_count (conn, config.db, config.coll, b);
+ ok (d > 0,
+ "mongo_sync_cmd_count() automatically reconnects");
+
+ bson_free (b);
+ mongo_sync_disconnect (conn);
+
+ test_mongo_sync_cmd_count_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_count (void)
+{
+ mongo_sync_connection *c;
+ bson *b;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ b = test_bson_generate_full ();
+
+ ok (mongo_sync_cmd_count (NULL, "test", "db", b) == -1,
+ "mongo_sync_cmd_count() fails with a NULL connection");
+ ok (mongo_sync_cmd_count (c, NULL, "db", b) == -1,
+ "mongo_sync_cmd_count() fails with a NULL db");
+ ok (mongo_sync_cmd_count (c, "test", NULL, b) == -1,
+ "mongo_sync_cmd_count() fails with a NULL collection");
+
+ ok (mongo_sync_cmd_count (c, "test", "db", b) == -1,
+ "mongo_sync_cmd_count() fails with a bogus FD");
+ mongo_sync_conn_set_slaveok (c, TRUE);
+ ok (mongo_sync_cmd_count (c, "test", "db", b) == -1,
+ "mongo_sync_cmd_count() fails with a bogus FD");
+
+ bson_free (b);
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_count_net ();
+}
+
+RUN_TEST (9, mongo_sync_cmd_count);
diff --git a/tests/unit/mongo/sync/sync_cmd_create.c b/tests/unit/mongo/sync/sync_cmd_create.c
new file mode 100644
index 0000000..c3334ea
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_create.c
@@ -0,0 +1,78 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_create_net (void)
+{
+ mongo_sync_connection *conn;
+ gchar *cc;
+
+ begin_network_tests (5);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+
+ cc = g_strconcat (config.coll, ".capped", NULL);
+
+ mongo_sync_cmd_drop (conn, config.db, config.coll);
+ mongo_sync_cmd_drop (conn, config.db, cc);
+
+ ok (mongo_sync_cmd_create (conn, config.db, config.coll,
+ MONGO_COLLECTION_DEFAULTS) == TRUE,
+ "mongo_sync_cmd_create() can create normal collections");
+ mongo_sync_cmd_drop (conn, config.db, config.coll);
+
+ ok (mongo_sync_cmd_create (conn, config.db, config.coll,
+ MONGO_COLLECTION_SIZED,
+ (gint64) 64 * 1024 * 10) == TRUE,
+ "mongo_sync_cmd_create() can create pre-allocated collections");
+
+ ok (mongo_sync_cmd_create (conn, config.db, cc,
+ MONGO_COLLECTION_CAPPED, (gint64) -1) == FALSE,
+ "mongo_sync_cmd_create() fails when trying to create a capped "
+ "collection with an invalid size");
+ ok (mongo_sync_cmd_create (conn, config.db, cc,
+ MONGO_COLLECTION_CAPPED_MAX,
+ (gint64) (64 * 1024 * 10), (gint64) -1) == FALSE,
+ "mongo_sync_cmd_create() fails when trying to create a capped "
+ "collection with invalid max.");
+ ok (mongo_sync_cmd_create (conn, config.db, cc,
+ MONGO_COLLECTION_CAPPED_MAX |
+ MONGO_COLLECTION_AUTO_INDEX_ID,
+ (gint64)(64 * 1024 * 10), (gint64) 10) == TRUE,
+ "mongo_sync_cmd_create() can create capped collections");
+
+ mongo_sync_cmd_drop (conn, config.db, cc);
+
+ g_free (cc);
+ mongo_sync_disconnect (conn);
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_create (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ ok (mongo_sync_cmd_create (NULL, "test", "db",
+ MONGO_COLLECTION_DEFAULTS) == FALSE,
+ "mongo_sync_cmd_create() fails with a NULL connection");
+
+ ok (mongo_sync_cmd_create (c, NULL, "db",
+ MONGO_COLLECTION_DEFAULTS) == FALSE,
+ "mongo_sync_cmd_create() fails with a NULL db");
+ ok (mongo_sync_cmd_create (c, "test", NULL,
+ MONGO_COLLECTION_DEFAULTS) == FALSE,
+ "mongo_sync_cmd_create() fails with a NULL collection");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_create_net ();
+}
+
+RUN_TEST (8, mongo_sync_cmd_create);
diff --git a/tests/unit/mongo/sync/sync_cmd_custom.c b/tests/unit/mongo/sync/sync_cmd_custom.c
new file mode 100644
index 0000000..1bd3f01
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_custom.c
@@ -0,0 +1,100 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_custom_net_secondary (void)
+{
+ mongo_sync_connection *conn;
+ bson *cmd;
+ mongo_packet *p;
+
+ skip (!config.secondary_host, 1,
+ "Secondary server not configured");
+
+ conn = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ cmd = bson_build (BSON_TYPE_INT32, "getnonce", 1,
+ BSON_TYPE_NONE);
+ bson_finish (cmd);
+
+ p = mongo_sync_cmd_custom (conn, config.db, cmd);
+ ok (p != NULL,
+ "mongo_sync_cmd_custom() works on the secondary too");
+ mongo_wire_packet_free (p);
+
+ bson_free (cmd);
+ mongo_sync_disconnect (conn);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_custom_net (void)
+{
+ mongo_sync_connection *conn;
+ bson *cmd;
+ mongo_packet *p;
+
+ begin_network_tests (3);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_cmd_is_master (conn);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ cmd = bson_build (BSON_TYPE_INT32, "getnonce", 1,
+ BSON_TYPE_NONE);
+ bson_finish (cmd);
+
+ p = mongo_sync_cmd_custom (conn, config.db, cmd);
+ ok (p != NULL,
+ "mongo_sync_cmd_custom() works");
+ mongo_wire_packet_free (p);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ p = mongo_sync_cmd_custom (conn, config.db, cmd);
+ ok (p != NULL,
+ "mongo_sync_cmd_custom() automatically reconnects");
+ mongo_wire_packet_free (p);
+
+ bson_free (cmd);
+ mongo_sync_disconnect (conn);
+
+ test_mongo_sync_cmd_custom_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_custom (void)
+{
+ mongo_sync_connection *c;
+ bson *cmd;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ cmd = bson_new ();
+ bson_append_int32 (cmd, "getnonce", 1);
+ bson_finish (cmd);
+
+ ok (mongo_sync_cmd_custom (NULL, "test", cmd) == NULL,
+ "mongo_sync_cmd_custom() fails with a NULL connection");
+ ok (mongo_sync_cmd_custom (c, NULL, cmd) == NULL,
+ "mongo_sync_cmd_custom() fails with a NULL namespace");
+
+ ok (mongo_sync_cmd_custom (c, "test", cmd) == NULL,
+ "mongo_sync_cmd_custom() fails with a bogus FD");
+ mongo_sync_conn_set_slaveok (c, TRUE);
+ ok (mongo_sync_cmd_custom (c, "test", cmd) == NULL,
+ "mongo_sync_cmd_custom() fails with a bogus FD");
+
+ bson_free (cmd);
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_custom_net ();
+}
+
+RUN_TEST (7, mongo_sync_cmd_custom);
diff --git a/tests/unit/mongo/sync/sync_cmd_delete.c b/tests/unit/mongo/sync/sync_cmd_delete.c
new file mode 100644
index 0000000..0c20ffe
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_delete.c
@@ -0,0 +1,135 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_delete_net_secondary (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+ GList *l;
+
+ skip (!config.secondary_host, 2,
+ "Secondary server not configured");
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+
+ b = bson_new ();
+ bson_append_string (b, "unit-test", __FILE__, -1);
+ bson_append_boolean (b, "delete-me", TRUE);
+ bson_finish (b);
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+
+ mongo_sync_disconnect (conn);
+
+ conn = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ ok (mongo_sync_cmd_delete (conn, config.ns, 0, b) == TRUE,
+ "mongo_sync_cmd_delete() can reconnect to master");
+ mongo_sync_disconnect (conn);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+ mongo_sync_disconnect (conn);
+
+ conn = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+
+ l = conn->rs.hosts;
+ while (l)
+ {
+ g_free (l->data);
+ l = g_list_delete_link (l, l);
+ }
+ conn->rs.hosts = NULL;
+
+ l = conn->rs.seeds;
+ while (l)
+ {
+ g_free (l->data);
+ l = g_list_delete_link (l, l);
+ }
+ conn->rs.seeds = NULL;
+
+ sleep (3);
+
+ ok (mongo_sync_cmd_delete (conn, config.ns, 0, b) == FALSE,
+ "mongo_sync_cmd_delete() fails if it can't reconnect to master");
+
+ mongo_sync_disconnect (conn);
+ bson_free (b);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_delete_net (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+
+ begin_network_tests (4);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ b = bson_new ();
+ bson_append_string (b, "unit-test", __FILE__, -1);
+ bson_append_boolean (b, "delete-me", TRUE);
+ bson_finish (b);
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+
+ ok (mongo_sync_cmd_delete (conn, config.ns, 0, b) == TRUE,
+ "mongo_sync_cmd_delete() works");
+
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_delete (conn, config.ns, 0, b) == TRUE,
+ "mongo_sync_cmd_delete() automatically reconnects");
+
+ mongo_sync_disconnect (conn);
+ bson_free (b);
+
+ test_mongo_sync_cmd_delete_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_delete (void)
+{
+ mongo_sync_connection *c;
+ bson *b;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ b = test_bson_generate_full ();
+
+ ok (mongo_sync_cmd_delete (NULL, "test.ns", 0, b) == FALSE,
+ "mongo_sync_cmd_delete() fails with a NULL connection");
+ ok (mongo_sync_cmd_delete (c, NULL, 0, b) == FALSE,
+ "mongo_sync_cmd_delete() fails with a NULL namespace");
+ ok (mongo_sync_cmd_delete (c, "test.ns", 0, NULL) == FALSE,
+ "mongo_sync_cmd_delete() fails with a NULL selector");
+
+ ok (mongo_sync_cmd_delete (c, "test.ns", 0, b) == FALSE,
+ "mongo_sync_cmd_delete() fails with a bogus FD");
+
+ bson_free (b);
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_delete_net ();
+}
+
+RUN_TEST (8, mongo_sync_cmd_delete);
diff --git a/tests/unit/mongo/sync/sync_cmd_drop.c b/tests/unit/mongo/sync/sync_cmd_drop.c
new file mode 100644
index 0000000..c7f9d9f
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_drop.c
@@ -0,0 +1,93 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_drop_net_secondary (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+ gboolean ret;
+
+ skip (!config.secondary_host, 1,
+ "Secondary server not configured");
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ b = bson_build (BSON_TYPE_BOOLEAN, "filler", TRUE,
+ BSON_TYPE_NONE);
+ bson_finish (b);
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+ bson_free (b);
+ mongo_sync_disconnect (conn);
+
+ conn = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ mongo_sync_cmd_is_master (conn);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ ret = mongo_sync_cmd_drop (conn, config.db, config.coll);
+ ok (ret && mongo_sync_cmd_is_master (conn),
+ "mongo_sync_cmd_drop() can reconnect to master");
+ mongo_sync_disconnect (conn);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_drop_net (void)
+{
+ mongo_sync_connection *conn;
+ bson *b;
+
+ begin_network_tests (3);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ b = bson_build (BSON_TYPE_BOOLEAN, "filler", TRUE,
+ BSON_TYPE_NONE);
+ bson_finish (b);
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+
+ ok (mongo_sync_cmd_drop (conn, config.db, config.coll) == TRUE,
+ "mongo_sync_cmd_drop() works");
+
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_drop (conn, config.db, config.coll) == TRUE,
+ "mongo_sync_cmd_drop() automatically reconnects");
+
+ bson_free (b);
+ mongo_sync_disconnect (conn);
+
+ test_mongo_sync_cmd_drop_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_drop (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ ok (mongo_sync_cmd_drop (NULL, "test", "db") == FALSE,
+ "mongo_sync_cmd_drop() fails with a NULL connection");
+ ok (mongo_sync_cmd_drop (c, NULL, "db") == FALSE,
+ "mongo_sync_cmd_drop() fails with a NULL db");
+
+ ok (mongo_sync_cmd_drop (c, "test", "db") == FALSE,
+ "mongo_sync_cmd_drop() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_drop_net ();
+}
+
+RUN_TEST (6, mongo_sync_cmd_drop);
diff --git a/tests/unit/mongo/sync/sync_cmd_exists.c b/tests/unit/mongo/sync/sync_cmd_exists.c
new file mode 100644
index 0000000..f3c535f
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_exists.c
@@ -0,0 +1,85 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_exists_net (void)
+{
+ mongo_sync_connection *conn;
+ gchar *cc, *ns;
+
+ bson *r;
+ bson_cursor *c;
+ const gchar *str = NULL;
+ gboolean capped = FALSE;
+
+ begin_network_tests (4);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, FALSE);
+
+ cc = g_strconcat (config.coll, ".capped", NULL);
+
+ mongo_sync_cmd_drop (conn, config.db, config.coll);
+ mongo_sync_cmd_drop (conn, config.db, cc);
+
+ mongo_sync_cmd_create (conn, config.db, config.coll,
+ MONGO_COLLECTION_DEFAULTS);
+ mongo_sync_cmd_create (conn, config.db, cc,
+ MONGO_COLLECTION_CAPPED,
+ (gint64) 64 * 1024 * 10);
+
+ r = mongo_sync_cmd_exists (conn, config.db, config.coll);
+ c = bson_find (r, "name");
+ bson_cursor_get_string (c, &str);
+ is (str, config.ns,
+ "mongo_sync_cmd_exists() works on normal collections");
+ bson_cursor_find (c, "capped");
+ bson_cursor_get_boolean (c, &capped);
+ cmp_ok (capped, "==", FALSE,
+ "mongo_sync_cmd_exists() returned correct info");
+ bson_cursor_free (c);
+ bson_free (r);
+
+ r = mongo_sync_cmd_exists (conn, config.db, cc);
+ ns = g_strconcat (config.db, ".", cc, NULL);
+ c = bson_find (r, "name");
+ bson_cursor_get_string (c, &str);
+ is (str, ns,
+ "mongo_sync_cmd_exists() works on capped collections");
+ bson_cursor_find (c, "capped");
+ bson_cursor_get_boolean (c, &capped);
+ cmp_ok (capped, "==", FALSE,
+ "mongo_sync_cmd_exists() returned correct info");
+ bson_cursor_free (c);
+ g_free (ns);
+ bson_free (r);
+
+ mongo_sync_cmd_drop (conn, config.db, cc);
+
+ g_free (cc);
+ mongo_sync_disconnect (conn);
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_exists (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ ok (mongo_sync_cmd_exists (NULL, "test", "db") == NULL,
+ "mongo_sync_cmd_exists() fails with a NULL connection");
+ ok (mongo_sync_cmd_exists (c, NULL, "db") == NULL,
+ "mongo_sync_cmd_exists() fails with a NULL db");
+ ok (mongo_sync_cmd_exists (c, "test", NULL) == NULL,
+ "mongo_sync_cmd_exists() fails with a NULL collection");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_exists_net ();
+}
+
+RUN_TEST (7, mongo_sync_cmd_exists);
diff --git a/tests/unit/mongo/sync/sync_cmd_get_last_error.c b/tests/unit/mongo/sync/sync_cmd_get_last_error.c
new file mode 100644
index 0000000..fef9f78
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_get_last_error.c
@@ -0,0 +1,35 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_cmd_get_last_error (void)
+{
+ mongo_sync_connection *c;
+ gchar *error;
+
+ test_env_setup ();
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_get_last_error (NULL, config.db, &error) == FALSE,
+ "mongo_sync_cmd_get_last_error() returns FALSE with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ ok (mongo_sync_cmd_get_last_error (c, NULL, &error) == FALSE,
+ "mongo_sync_cmd_get_last_error() fails with a NULL db");
+
+ errno = 0;
+ ok (mongo_sync_cmd_get_last_error (c, config.db, NULL) == FALSE,
+ "mongo_sync_cmd_get_last_error() fails with a NULL error destination");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ mongo_sync_disconnect (c);
+ test_env_free ();
+}
+
+RUN_TEST (5, mongo_sync_cmd_get_last_error);
diff --git a/tests/unit/mongo/sync/sync_cmd_get_last_error_full.c b/tests/unit/mongo/sync/sync_cmd_get_last_error_full.c
new file mode 100644
index 0000000..505fd3d
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_get_last_error_full.c
@@ -0,0 +1,35 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_cmd_get_last_error_full (void)
+{
+ mongo_sync_connection *c;
+ bson *error;
+
+ test_env_setup ();
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_get_last_error_full (NULL, config.db, &error) == FALSE,
+ "mongo_sync_cmd_get_last_error_full() returns FALSE with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ ok (mongo_sync_cmd_get_last_error_full (c, NULL, &error) == FALSE,
+ "mongo_sync_cmd_get_last_error_full() fails with a NULL db");
+
+ errno = 0;
+ ok (mongo_sync_cmd_get_last_error_full (c, config.db, NULL) == FALSE,
+ "mongo_sync_cmd_get_last_error_full() fails with a NULL error destination");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ mongo_sync_disconnect (c);
+ test_env_free ();
+}
+
+RUN_TEST (5, mongo_sync_cmd_get_last_error_full);
diff --git a/tests/unit/mongo/sync/sync_cmd_get_more.c b/tests/unit/mongo/sync/sync_cmd_get_more.c
new file mode 100644
index 0000000..18a2f97
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_get_more.c
@@ -0,0 +1,135 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_get_more_net_secondary (void)
+{
+ mongo_packet *p;
+ mongo_sync_connection *conn;
+ bson *b;
+
+ mongo_reply_packet_header rh;
+ gint64 cid;
+
+ skip (!config.secondary_host, 2,
+ "Secondary server not configured");
+
+ conn = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ b = bson_new ();
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_finish (b);
+
+ p = mongo_sync_cmd_query (conn, config.ns,
+ MONGO_WIRE_FLAG_QUERY_NO_CURSOR_TIMEOUT,
+ 0, 2, b, NULL);
+ bson_free (b);
+ mongo_wire_reply_packet_get_header (p, &rh);
+ cid = rh.cursor_id;
+ mongo_wire_packet_free (p);
+
+ p = mongo_sync_cmd_get_more (conn, config.db, 3, cid);
+ ok (p != NULL,
+ "mongo_sync_cmd_get_more() works on secondary too");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_reconnect (conn, TRUE);
+
+ p = mongo_sync_cmd_get_more (conn, config.db, 10, cid);
+ ok (p == NULL && errno == EPROTO,
+ "mongo_sync_cmd_get_more() can't jump servers");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_disconnect (conn);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_get_more_net (void)
+{
+ mongo_packet *p;
+ mongo_sync_connection *conn;
+ bson *b;
+ gint i;
+ mongo_reply_packet_header rh;
+ gint64 cid;
+
+ begin_network_tests (4);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ b = bson_new ();
+ for (i = 0; i < 40; i++)
+ {
+ bson_reset (b);
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_append_int32 (b, "seq", i);
+ bson_finish (b);
+
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+ }
+ bson_free (b);
+
+ b = bson_new ();
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_finish (b);
+
+ p = mongo_sync_cmd_query (conn, config.ns,
+ MONGO_WIRE_FLAG_QUERY_NO_CURSOR_TIMEOUT,
+ 0, 2, b, NULL);
+ bson_free (b);
+ mongo_wire_reply_packet_get_header (p, &rh);
+ cid = rh.cursor_id;
+ mongo_wire_packet_free (p);
+
+ p = mongo_sync_cmd_get_more (conn, config.ns, 3, cid);
+ ok (p != NULL,
+ "mongo_sync_cmd_get_more() works");
+ mongo_wire_packet_free (p);
+
+ errno = 0;
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ p = mongo_sync_cmd_get_more (conn, config.ns, 10, cid);
+ ok (p != NULL,
+ "mongo_sync_cmd_get_more() automatically reconnects");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_disconnect (conn);
+
+ test_mongo_sync_cmd_get_more_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_get_more (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ ok (mongo_sync_cmd_get_more (NULL, "test.ns", 1, 1234) == NULL,
+ "mongo_sync_cmd_get_more() fails with a NULL connection");
+ ok (mongo_sync_cmd_get_more (c, NULL, 1, 1234) == NULL,
+ "mongo_sync_cmd_get_more() fails with a NULL namespace");
+
+ ok (mongo_sync_cmd_get_more (c, "test.ns", 1, 1234) == NULL,
+ "mongo_sync_cmd_get_more() fails with a bogus FD");
+ mongo_sync_conn_set_slaveok (c, TRUE);
+ ok (mongo_sync_cmd_get_more (c, "test.ns", 1, 1234) == NULL,
+ "mongo_sync_cmd_get_more() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_get_more_net ();
+}
+
+RUN_TEST (8, mongo_sync_cmd_get_more);
diff --git a/tests/unit/mongo/sync/sync_cmd_index_create.c b/tests/unit/mongo/sync/sync_cmd_index_create.c
new file mode 100644
index 0000000..6603586
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_index_create.c
@@ -0,0 +1,62 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_index_create (void)
+{
+ mongo_sync_connection *c;
+ bson *doc, *indexes, *bad_index;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ doc = test_bson_generate_full ();
+ indexes = bson_build (BSON_TYPE_INT32, "sex", 1,
+ BSON_TYPE_DOUBLE, "double", 1.0,
+ BSON_TYPE_BOOLEAN, "TRUE", TRUE,
+ BSON_TYPE_INT64, "print", (gint64)-1,
+ BSON_TYPE_INT32, "zero", 0,
+ BSON_TYPE_NONE);
+ bson_finish (indexes);
+
+ bad_index = bson_build (BSON_TYPE_STRING, "str", "teapot", -1,
+ BSON_TYPE_NONE);
+ bson_finish (bad_index);
+
+ ok (mongo_sync_cmd_index_create (NULL, "test.ns", indexes, 0) == FALSE,
+ "mongo_sync_cmd_index_create() fails with a NULL connection");
+ ok (mongo_sync_cmd_index_create (c, NULL, indexes, 0) == FALSE,
+ "mongo_sync_cmd_index_create() fails with a NULL namespace");
+ ok (mongo_sync_cmd_index_create (c, "test.ns", NULL, 0) == FALSE,
+ "mongo_sync_cmd_index_create() fails with NULL indexes");
+ ok (mongo_sync_cmd_index_create (c, "bogus", indexes, 0) == FALSE,
+ "mongo_sync_cmd_index_create() fails with a bogus namespace");
+ ok (mongo_sync_cmd_index_create (c, "test.ns", indexes, 0) == FALSE,
+ "mongo_sync_cmd_index_create() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ begin_network_tests (2);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+ mongo_sync_cmd_insert (c, config.ns, doc, NULL);
+
+ ok (mongo_sync_cmd_index_create(c, config.ns, indexes,
+ MONGO_INDEX_UNIQUE | MONGO_INDEX_DROP_DUPS |
+ MONGO_INDEX_BACKGROUND | MONGO_INDEX_SPARSE),
+ "mongo_sync_cmd_index_create() works");
+
+ ok (mongo_sync_cmd_index_create(c, config.ns, bad_index, 0) == FALSE,
+ "mongo_sync_cmd_index_create() should refuse to work with an invalid index spec");
+
+ mongo_sync_disconnect (c);
+
+ bson_free (doc);
+ bson_free (indexes);
+
+ end_network_tests ();
+}
+
+RUN_TEST (7, mongo_sync_cmd_index_create);
diff --git a/tests/unit/mongo/sync/sync_cmd_index_drop.c b/tests/unit/mongo/sync/sync_cmd_index_drop.c
new file mode 100644
index 0000000..176de6d
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_index_drop.c
@@ -0,0 +1,51 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_cmd_index_drop (void)
+{
+ mongo_sync_connection *c;
+ bson *doc, *indexes;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ doc = test_bson_generate_full ();
+ indexes = bson_build (BSON_TYPE_INT32, "sex", 1,
+ BSON_TYPE_DOUBLE, "double", 1.0,
+ BSON_TYPE_BOOLEAN, "TRUE", TRUE,
+ BSON_TYPE_INT64, "print", (gint64)-1,
+ BSON_TYPE_NONE);
+ bson_finish (indexes);
+
+ ok (mongo_sync_cmd_index_drop (NULL, "test.ns", indexes) == FALSE,
+ "mongo_sync_cmd_index_drop() fails with a NULL connection");
+ ok (mongo_sync_cmd_index_drop (c, NULL, indexes) == FALSE,
+ "mongo_sync_cmd_index_drop() fails with a NULL namespace");
+ ok (mongo_sync_cmd_index_drop (c, "test.ns", NULL) == FALSE,
+ "mongo_sync_cmd_index_drop() fails with NULL indexes");
+ ok (mongo_sync_cmd_index_drop (c, "bogus", indexes) == FALSE,
+ "mongo_sync_cmd_index_drop() fails with a bogus namespace");
+ ok (mongo_sync_cmd_index_drop (c, "test.ns", indexes) == FALSE,
+ "mongo_sync_cmd_index_drop() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ begin_network_tests (1);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+ mongo_sync_cmd_insert (c, config.ns, doc, NULL);
+
+ mongo_sync_cmd_index_create (c, config.ns, indexes, 0);
+
+ ok (mongo_sync_cmd_index_drop (c, config.ns, indexes) == TRUE,
+ "mongo_sync_cmd_index_drop() works");
+
+ mongo_sync_disconnect (c);
+
+ bson_free (doc);
+ bson_free (indexes);
+
+ end_network_tests ();
+}
+
+RUN_TEST (6, mongo_sync_cmd_index_drop);
diff --git a/tests/unit/mongo/sync/sync_cmd_index_drop_all.c b/tests/unit/mongo/sync/sync_cmd_index_drop_all.c
new file mode 100644
index 0000000..782fd93
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_index_drop_all.c
@@ -0,0 +1,49 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_cmd_index_drop_all (void)
+{
+ mongo_sync_connection *c;
+ bson *doc, *indexes;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ doc = test_bson_generate_full ();
+ indexes = bson_build (BSON_TYPE_INT32, "sex", 1,
+ BSON_TYPE_DOUBLE, "double", 1.0,
+ BSON_TYPE_BOOLEAN, "TRUE", TRUE,
+ BSON_TYPE_INT64, "print", (gint64)-1,
+ BSON_TYPE_NONE);
+ bson_finish (indexes);
+
+ ok (mongo_sync_cmd_index_drop_all (NULL, "test.ns") == FALSE,
+ "mongo_sync_cmd_index_drop_all() fails with a NULL connection");
+ ok (mongo_sync_cmd_index_drop_all (c, NULL) == FALSE,
+ "mongo_sync_cmd_index_drop_all() fails with a NULL namespace");
+ ok (mongo_sync_cmd_index_drop_all (c, "bogus") == FALSE,
+ "mongo_sync_cmd_index_drop_all() fails with a bogus namespace");
+ ok (mongo_sync_cmd_index_drop_all (c, "test.ns") == FALSE,
+ "mongo_sync_cmd_index_drop_all() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ begin_network_tests (1);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+ mongo_sync_cmd_insert (c, config.ns, doc, NULL);
+
+ mongo_sync_cmd_index_create (c, config.ns, indexes, 0);
+
+ ok (mongo_sync_cmd_index_drop_all (c, config.ns) == TRUE,
+ "mongo_sync_cmd_index_drop_all() works");
+
+ mongo_sync_disconnect (c);
+
+ bson_free (doc);
+ bson_free (indexes);
+
+ end_network_tests ();
+}
+
+RUN_TEST (5, mongo_sync_cmd_index_drop_all);
diff --git a/tests/unit/mongo/sync/sync_cmd_insert.c b/tests/unit/mongo/sync/sync_cmd_insert.c
new file mode 100644
index 0000000..f9a0f6b
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_insert.c
@@ -0,0 +1,78 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_insert (void)
+{
+ mongo_sync_connection *c;
+ bson *b1, *b2;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ b1 = test_bson_generate_full ();
+ b2 = test_bson_generate_full ();
+
+ ok (mongo_sync_cmd_insert (NULL, "test.ns", b1, b2, NULL) == FALSE,
+ "mongo_sync_cmd_insert() fails with a NULL connection");
+ ok (mongo_sync_cmd_insert (c, NULL, b1, b2, NULL) == FALSE,
+ "mongo_sync_cmd_insert() fails with a NULL namespace");
+ ok (mongo_sync_cmd_insert (c, "test.ns", NULL) == FALSE,
+ "mongo_sync_cmd_insert() fails with no documents to insert");
+ ok (mongo_sync_cmd_insert (c, "test.ns", b1, b2, NULL) == FALSE,
+ "mongo_sync_cmd_insert() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+ bson_free (b1);
+ bson_free (b2);
+
+ begin_network_tests (4);
+
+ b1 = bson_new ();
+ bson_append_string (b1, "sync_cmd_insert", "works", -1);
+ bson_finish (b1);
+ b2 = bson_new ();
+ bson_append_int32 (b2, "int32", 1984);
+ bson_finish (b2);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (mongo_sync_cmd_insert (c, config.ns, b1, b2, NULL) == TRUE,
+ "mongo_sync_cmd_insert() works");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_insert (c, config.ns, b1, b2, NULL) == TRUE,
+ "mongo_sync_cmd_insert() automatically reconnects");
+
+ mongo_sync_disconnect (c);
+
+ /*
+ * Tests involving a secondary
+ */
+ skip (!config.secondary_host, 2, "Secondary host not set up");
+
+ c = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (c && mongo_sync_cmd_is_master (c) == FALSE,
+ "Connected to a secondary");
+
+ ok (mongo_sync_cmd_insert (c, config.ns, b1, b2, NULL) == TRUE,
+ "mongo_sync_cmd_insert() automatically reconnects to master");
+ mongo_sync_disconnect (c);
+
+ endskip;
+
+ bson_free (b1);
+ bson_free (b2);
+
+ end_network_tests ();
+}
+
+RUN_TEST (8, mongo_sync_cmd_insert);
diff --git a/tests/unit/mongo/sync/sync_cmd_insert_n.c b/tests/unit/mongo/sync/sync_cmd_insert_n.c
new file mode 100644
index 0000000..9281c17
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_insert_n.c
@@ -0,0 +1,100 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_insert_n (void)
+{
+ mongo_sync_connection *c;
+ bson *b1, *b2, *b3;
+ const bson *docs[10];
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ b1 = test_bson_generate_full ();
+ b2 = test_bson_generate_full ();
+ b3 = bson_new ();
+
+ docs[0] = b1;
+ docs[1] = b2;
+ docs[2] = b3;
+ docs[3] = NULL;
+ docs[4] = b1;
+
+ ok (mongo_sync_cmd_insert_n (NULL, "test.ns", 3, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() fails with a NULL connection");
+ ok (mongo_sync_cmd_insert_n (c, NULL, 3, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() fails with a NULL namespace");
+ ok (mongo_sync_cmd_insert_n (c, "test.ns", 0, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() fails with no documents to insert");
+ ok (mongo_sync_cmd_insert_n (c, "test.ns", 3, NULL) == FALSE,
+ "mongo_sync_cmd_insert_n() fails with no documents to insert");
+ ok (mongo_sync_cmd_insert_n (c, "test.ns", 3, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() fails when the array contains an "
+ "unfinished document");
+ bson_finish (b3);
+ ok (mongo_sync_cmd_insert_n (c, "test.ns", 5, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() fails when the array contains a "
+ "NULL document");
+ ok (mongo_sync_cmd_insert_n (c, "test.ns", 3, docs) == FALSE,
+ "mongo_sync_cmd_insert_n() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+ bson_free (b1);
+ bson_free (b2);
+ bson_free (b3);
+
+ begin_network_tests (4);
+
+ b1 = bson_new ();
+ bson_append_string (b2, "sync_cmd_insert_n", "works", -1);
+ bson_finish (b1);
+
+ b2 = bson_new ();
+ bson_append_int32 (b2, "int32", 1984);
+ bson_finish (b2);
+
+ docs[0] = b1;
+ docs[1] = b2;
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port,
+ TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (mongo_sync_cmd_insert_n (c, config.ns, 2, docs) == TRUE,
+ "mongo_sync_cmd_insert_n() works");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_insert_n (c, config.ns, 2, docs) == TRUE,
+ "mongo_sync_cmd_insert_n() automatically reconnects");
+
+ mongo_sync_disconnect (c);
+
+ /*
+ * Tests involving a secondary
+ */
+ skip (!config.secondary_host, 2, "Secondary host not set up");
+
+ c = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (c && mongo_sync_cmd_is_master (c) == FALSE,
+ "Connected to a secondary");
+
+ ok (mongo_sync_cmd_insert_n (c, config.ns, 2, docs) == TRUE,
+ "mongo_sync_cmd_insert_n() automatically reconnects to master");
+ mongo_sync_disconnect (c);
+
+ endskip;
+
+ bson_free (b1);
+ bson_free (b2);
+
+ end_network_tests ();
+}
+
+RUN_TEST (11, mongo_sync_cmd_insert_n);
diff --git a/tests/unit/mongo/sync/sync_cmd_is_master.c b/tests/unit/mongo/sync/sync_cmd_is_master.c
new file mode 100644
index 0000000..6fa8bb4
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_is_master.c
@@ -0,0 +1,65 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_cmd_is_master_net_secondary (void)
+{
+ mongo_sync_connection *conn;
+
+ skip (!config.secondary_host, 1,
+ "Secondary server not configured");
+
+ errno = 0;
+ conn = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ ok (mongo_sync_cmd_is_master (conn) == FALSE && errno == 0,
+ "mongo_sync_cmd_is_master() works correctly on a secondary");
+ mongo_sync_disconnect (conn);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_is_master_net (void)
+{
+ mongo_sync_connection *conn;
+
+ begin_network_tests (2);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ ok (mongo_sync_cmd_is_master (conn) == TRUE,
+ "mongo_sync_cmd_is_master() works");
+ mongo_sync_disconnect (conn);
+
+ test_mongo_sync_cmd_is_master_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_is_master (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_is_master (NULL) == FALSE,
+ "mongo_sync_cmd_is_master fails with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ errno = 0;
+ ok (mongo_sync_cmd_is_master (c) == FALSE,
+ "mongo_sync_cmd_is_master() works");
+ cmp_ok (errno, "!=", 0,
+ "errno is not 0");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_is_master_net ();
+}
+
+RUN_TEST (6, mongo_sync_cmd_is_master);
diff --git a/tests/unit/mongo/sync/sync_cmd_kill_cursors.c b/tests/unit/mongo/sync/sync_cmd_kill_cursors.c
new file mode 100644
index 0000000..c23a5d8
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_kill_cursors.c
@@ -0,0 +1,123 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_kill_cursors_net_secondary (void)
+{
+ mongo_packet *p;
+ mongo_sync_connection *conn;
+ bson *b;
+
+ mongo_reply_packet_header rh;
+ gint64 cid;
+
+ skip (!config.secondary_host, 1,
+ "Secondary server not configured");
+
+ conn = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ b = bson_new ();
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_finish (b);
+
+ p = mongo_sync_cmd_query (conn, config.ns,
+ MONGO_WIRE_FLAG_QUERY_NO_CURSOR_TIMEOUT,
+ 0, 2, b, NULL);
+ bson_free (b);
+ mongo_wire_reply_packet_get_header (p, &rh);
+ cid = rh.cursor_id;
+ mongo_wire_packet_free (p);
+
+ ok (mongo_sync_cmd_kill_cursors (conn, 1, cid) == TRUE,
+ "mongo_sync_cmd_kill_cursors() works on secondary too");
+
+ mongo_sync_disconnect (conn);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_kill_cursors_net (void)
+{
+ mongo_packet *p;
+ mongo_sync_connection *conn;
+ bson *b;
+ gint i;
+ mongo_reply_packet_header rh;
+ gint64 cid;
+
+ begin_network_tests (3);
+
+ conn = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (conn, TRUE);
+
+ b = bson_new ();
+ for (i = 0; i < 40; i++)
+ {
+ bson_reset (b);
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_append_int32 (b, "seq", i);
+ bson_finish (b);
+
+ mongo_sync_cmd_insert (conn, config.ns, b, NULL);
+ }
+ bson_free (b);
+
+ b = bson_new ();
+ bson_append_string (b, "test-name", __FILE__, -1);
+ bson_finish (b);
+
+ p = mongo_sync_cmd_query (conn, config.ns,
+ MONGO_WIRE_FLAG_QUERY_NO_CURSOR_TIMEOUT,
+ 0, 2, b, NULL);
+ mongo_wire_reply_packet_get_header (p, &rh);
+ cid = rh.cursor_id;
+ mongo_wire_packet_free (p);
+
+ ok (mongo_sync_cmd_kill_cursors (conn, 1, cid) == TRUE,
+ "mongo_sync_kill_cursors() works");
+
+ p = mongo_sync_cmd_query (conn, config.ns,
+ MONGO_WIRE_FLAG_QUERY_NO_CURSOR_TIMEOUT,
+ 0, 2, b, NULL);
+ bson_free (b);
+ mongo_wire_reply_packet_get_header (p, &rh);
+ cid = rh.cursor_id;
+ mongo_wire_packet_free (p);
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_kill_cursors (conn, 1, cid) == TRUE,
+ "mongo_sync_cmd_kill_cursors() automatically reconnects");
+
+ mongo_sync_disconnect (conn);
+
+ test_mongo_sync_cmd_kill_cursors_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_kill_cursors (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ ok (mongo_sync_cmd_kill_cursors (NULL, 1, (gint64)1234) == FALSE,
+ "mongo_sync_cmd_kill_cursors() fails with a NULL connection");
+ ok (mongo_sync_cmd_kill_cursors (c, 0, (gint64)1234) == FALSE,
+ "mongo_sync_cmd_kill_cursors() fails with a negative number of cursors");
+
+ ok (mongo_sync_cmd_kill_cursors (c, 1, (gint64)1234) == FALSE,
+ "mongo_sync_cmd_kill_cursors() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_kill_cursors_net ();
+}
+
+RUN_TEST (6, mongo_sync_cmd_kill_cursors);
diff --git a/tests/unit/mongo/sync/sync_cmd_ping.c b/tests/unit/mongo/sync/sync_cmd_ping.c
new file mode 100644
index 0000000..51a8aaf
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_ping.c
@@ -0,0 +1,81 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_ping_net_secondary (void)
+{
+ mongo_sync_connection *c;
+
+ skip (!config.secondary_host, 2,
+ "Secondary server not configured");
+
+ c = mongo_sync_connect (config.secondary_host, config.secondary_port, TRUE);
+
+ ok (mongo_sync_cmd_ping (c) == TRUE,
+ "mongo_sync_cmd_ping() works");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_ping (c) == FALSE,
+ "mongo_sync_cmd_ping() returns FALSE when not connected");
+
+ mongo_sync_disconnect (c);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_ping_net (void)
+{
+ mongo_sync_connection *c;
+
+ begin_network_tests (4);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+
+ ok (mongo_sync_cmd_ping (c) == TRUE,
+ "mongo_sync_cmd_ping() works");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_ping (c) == FALSE,
+ "mongo_sync_cmd_ping() returns FALSE when not connected");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_ping_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_ping (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_ping (NULL) == FALSE,
+ "mongo_sync_cmd_ping(NULL) returns FALSE");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ errno = 0;
+ ok (mongo_sync_cmd_ping (c) == FALSE,
+ "Pinging a bogus connection fails");
+ cmp_ok (errno, "!=", 0,
+ "errno is not 0");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_ping_net ();
+}
+
+RUN_TEST (8, mongo_sync_cmd_ping);
diff --git a/tests/unit/mongo/sync/sync_cmd_query.c b/tests/unit/mongo/sync/sync_cmd_query.c
new file mode 100644
index 0000000..da7c693
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_query.c
@@ -0,0 +1,125 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_query (void)
+{
+ mongo_packet *p;
+ mongo_sync_connection *c;
+ bson *q, *s;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+ q = test_bson_generate_full ();
+ s = test_bson_generate_full ();
+
+ ok (mongo_sync_cmd_query (NULL, "test.ns", 0, 0, 1, q, s) == NULL,
+ "mongo_sync_cmd_query() fails with a NULL connection");
+ ok (mongo_sync_cmd_query (c, NULL, 0, 0, 1, q, s) == NULL,
+ "mongo_sync_cmd_query() fails with a NULL namespace");
+ ok (mongo_sync_cmd_query (c, "test.ns", 0, 0, 1, NULL, s) == NULL,
+ "mongo_sync_cmd_query() fails with a NULL query");
+
+ ok (mongo_sync_cmd_query (c, "test.ns", 0, 0, 1, q, s) == NULL,
+ "mongo_sync_cmd_query() fails with a bogus FD");
+ mongo_sync_conn_set_slaveok (c, TRUE);
+ ok (mongo_sync_cmd_query (c, "test.ns", 0, 0, 1, q, s) == NULL,
+ "mongo_sync_cmd_query() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ bson_free (q);
+ bson_free (s);
+
+ begin_network_tests (7);
+
+ q = bson_new ();
+ bson_append_boolean (q, "sync_cmd_query_test", TRUE);
+ bson_finish (q);
+
+ s = bson_new ();
+ bson_append_boolean (s, "sync_cmd_query_test", FALSE);
+ bson_finish (s);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+ mongo_sync_cmd_insert (c, config.ns, q, NULL);
+
+ p = mongo_sync_cmd_query (c, config.ns, 0, 0, 1, q, NULL);
+ ok (p != NULL,
+ "mongo_sync_cmd_query() works");
+ mongo_wire_packet_free (p);
+
+ errno = 0;
+ p = mongo_sync_cmd_query (c, config.ns, 0, 0, 1, s, NULL);
+ ok (p == NULL && errno == ENOENT,
+ "mongo_sync_cmd_query() sets errno to ENOENT when there's "
+ "nothing to return");
+ mongo_wire_packet_free (p);
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ p = mongo_sync_cmd_query (c, config.ns, 0, 0, 1, q, NULL);
+ ok (p != NULL,
+ "mongo_sync_cmd_query() automatically reconnects");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_disconnect (c);
+
+ /*
+ * Test request/response pairing, by sending a crafted query first,
+ * and another, without reading the response for the first before
+ * that.
+ */
+ c = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ p = mongo_wire_cmd_query (12345, config.ns, MONGO_WIRE_FLAG_QUERY_SLAVE_OK,
+ 0, 1, s, NULL);
+ mongo_packet_send ((mongo_connection *)c, p);
+ mongo_wire_packet_free (p);
+
+ errno = 0;
+ p = mongo_sync_cmd_query (c, config.ns, 0, 0, 1, s, NULL);
+ ok (p == NULL && errno == EPROTO,
+ "mongo_sync_cmd_query() fails if the reply is not a response to "
+ "the current query");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_disconnect (c);
+
+ /*
+ * Tests involving a secondary
+ */
+ skip (!config.secondary_host, 3, "Secondary host not set up");
+
+ c = mongo_sync_connect (config.secondary_host, config.secondary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (c && mongo_sync_cmd_is_master (c) == FALSE,
+ "Connected to a secondary");
+ p = mongo_sync_cmd_query (c, config.ns, 0, 0, 1, q, NULL);
+ ok (p != NULL,
+ "mongo_sync_cmd_query() works on secondary");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_conn_set_slaveok (c, FALSE);
+
+ p = mongo_sync_cmd_query (c, config.ns, 0, 0, 1, q, NULL);
+ ok (p != NULL && mongo_sync_cmd_is_master (c) == TRUE,
+ "mongo_sync_cmd_query() can resync to master");
+ mongo_wire_packet_free (p);
+
+ mongo_sync_disconnect (c);
+
+ endskip;
+
+ bson_free (q);
+ bson_free (s);
+
+ end_network_tests ();
+}
+
+RUN_TEST (12, mongo_sync_cmd_query);
diff --git a/tests/unit/mongo/sync/sync_cmd_reset_error.c b/tests/unit/mongo/sync/sync_cmd_reset_error.c
new file mode 100644
index 0000000..8f92fcf
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_reset_error.c
@@ -0,0 +1,31 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_cmd_reset_error (void)
+{
+ mongo_sync_connection *c;
+
+ test_env_setup ();
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_reset_error (NULL, config.db) == FALSE,
+ "mongo_sync_cmd_reset_error() fails with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ ok (mongo_sync_cmd_reset_error (c, NULL) == FALSE,
+ "mongo_sync_cmd_reset_error() fails with a NULL db");
+
+ ok (mongo_sync_cmd_reset_error (c, config.db) == FALSE,
+ "mongo_sync_cmd_reset_error() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+ test_env_free ();
+}
+
+RUN_TEST (4, mongo_sync_cmd_reset_error);
diff --git a/tests/unit/mongo/sync/sync_cmd_update.c b/tests/unit/mongo/sync/sync_cmd_update.c
new file mode 100644
index 0000000..21b981f
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_update.c
@@ -0,0 +1,97 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+#include <sys/socket.h>
+
+void
+test_mongo_sync_cmd_update (void)
+{
+ mongo_sync_connection *c;
+ bson *sel, *upd;
+ guint8 *oid;
+
+ mongo_util_oid_init (0);
+
+ sel = bson_new ();
+ oid = mongo_util_oid_new (0);
+ bson_append_oid (sel, "_id", oid);
+ g_free (oid);
+ bson_finish (sel);
+
+ upd = test_bson_generate_full ();
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ ok (mongo_sync_cmd_update (NULL, "test.ns", 0, sel, upd) == FALSE,
+ "mongo_sync_cmd_update() fails with a NULL connection");
+ ok (mongo_sync_cmd_update (c, NULL, 0, sel, upd) == FALSE,
+ "mongo_sync_cmd_update() fails with a NULL namespace");
+ ok (mongo_sync_cmd_update (c, "test.ns", 0, NULL, upd) == FALSE,
+ "mongo_sync_cmd_update() fails with a NULL selector");
+ ok (mongo_sync_cmd_update (c, "test.ns", 0, sel, NULL) == FALSE,
+ "mongo_sync_cmd_update() fails with a NULL update");
+
+ ok (mongo_sync_cmd_update (c, "test.ns", 0, sel, upd) == FALSE,
+ "mongo_sync_cmd_update() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+ bson_free (sel);
+ bson_free (upd);
+
+ begin_network_tests (4);
+
+ sel = bson_new ();
+ oid = mongo_util_oid_new (1);
+ bson_append_oid (sel, "_id", oid);
+ g_free (oid);
+ bson_finish (sel);
+
+ upd = bson_new ();
+ oid = mongo_util_oid_new (1);
+ bson_append_oid (upd, "_id", oid);
+ g_free (oid);
+ bson_finish (upd);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port,
+ FALSE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (mongo_sync_cmd_update (c, config.ns,
+ MONGO_WIRE_FLAG_UPDATE_UPSERT, sel, upd) == TRUE,
+ "mongo_sync_cmd_update() works");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_update (c, config.ns,
+ MONGO_WIRE_FLAG_UPDATE_UPSERT, sel, upd) == TRUE,
+ "mongo_sync_cmd_update() automatically reconnects");
+
+ mongo_sync_disconnect (c);
+
+ /*
+ * Tests involving a secondary
+ */
+ skip (!config.secondary_host, 2,
+ "Secondary host not set up");
+
+ c = mongo_sync_connect (config.secondary_host, config.secondary_port,
+ TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (mongo_sync_cmd_is_master (c) == FALSE,
+ "Connected to a secondary");
+
+ ok (mongo_sync_cmd_update (c, config.ns,
+ MONGO_WIRE_FLAG_UPDATE_UPSERT, sel, upd) == TRUE,
+ "mongo_sync_cmd_update() automatically reconnects to master");
+ mongo_sync_disconnect (c);
+ endskip;
+
+ bson_free (sel);
+ bson_free (upd);
+ end_network_tests ();
+}
+
+RUN_TEST (9, mongo_sync_cmd_update);
diff --git a/tests/unit/mongo/sync/sync_cmd_user_add.c b/tests/unit/mongo/sync/sync_cmd_user_add.c
new file mode 100644
index 0000000..9cdc542
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_user_add.c
@@ -0,0 +1,95 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_user_add_net_secondary (void)
+{
+ mongo_sync_connection *c;
+ gboolean ret;
+
+ skip (!config.secondary_host, 1,
+ "Secondary server not configured");
+
+ c = mongo_sync_connect (config.secondary_host, config.secondary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ret = mongo_sync_cmd_user_add (c, config.db, "test", "s3kr1+");
+ ok (ret && mongo_sync_cmd_is_master (c),
+ "mongo_sync_cmd_user_add() automatically reconnects to master");
+
+ mongo_sync_disconnect (c);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_user_add_net (void)
+{
+ mongo_sync_connection *c;
+
+ begin_network_tests (3);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (mongo_sync_cmd_user_add (c, config.db, "test", "s3kr1+") == TRUE,
+ "mongo_sync_cmd_user_add() works");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_user_add (c, config.db, "test", "s3kr1+") == TRUE,
+ "mongo_sync_cmd_user_add() automatically reconnects");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_user_add_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_user_add (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_add (NULL, "test", "test", "s3kr1+") == FALSE,
+ "mongo_sync_cmd_user_add() fails with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_add (c, NULL, "test", "s3kr1+") == FALSE,
+ "mongo_sync_cmd_user_add() fails with a NULL db");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_add (c, "test", NULL, "s3kr1+") == FALSE,
+ "mongo_sync_cmd_user_add() fails with a NULL user");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_add (c, "test", "test", NULL) == FALSE,
+ "mongo_sync_cmd_user_add() fails with a NULL password");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ ok (mongo_sync_cmd_user_add (c, "test", "test", "s3kr1+") == FALSE,
+ "mongo_sync_cmd_user_add() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_user_add_net ();
+}
+
+RUN_TEST (12, mongo_sync_cmd_user_add);
diff --git a/tests/unit/mongo/sync/sync_cmd_user_add_with_roles.c b/tests/unit/mongo/sync/sync_cmd_user_add_with_roles.c
new file mode 100644
index 0000000..04bb842
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_user_add_with_roles.c
@@ -0,0 +1,89 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_user_add_with_roles_net (const bson *roles)
+{
+ mongo_sync_connection *c;
+
+ begin_network_tests (2);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ ok (mongo_sync_cmd_user_add_with_roles (c, config.db,
+ "test", "s3kr1+", roles) == TRUE,
+ "mongo_sync_cmd_user_add_with_roles() works");
+
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_user_add_with_roles (c, config.db,
+ "test", "s3kr1+", roles) == TRUE,
+ "mongo_sync_cmd_user_add_with_roles() automatically reconnects");
+
+ mongo_sync_disconnect (c);
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_user_add_with_roles (void)
+{
+ mongo_sync_connection *c;
+ bson *roles = bson_build (BSON_TYPE_STRING, "0", "readWrite", -1,
+ BSON_TYPE_NONE);
+
+ bson_finish (roles);
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_add_with_roles (NULL, "test",
+ "test", "s3kr1+", roles) == FALSE,
+ "mongo_sync_cmd_user_add_with_roles() fails with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_add_with_roles (c, NULL,
+ "test", "s3kr1+", roles) == FALSE,
+ "mongo_sync_cmd_user_add_with_roles() fails with a NULL db");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_add_with_roles (c, "test",
+ NULL, "s3kr1+", roles) == FALSE,
+ "mongo_sync_cmd_user_add_with_roles() fails with a NULL user");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_add_with_roles (c, "test",
+ "test", NULL, roles) == FALSE,
+ "mongo_sync_cmd_user_add_with_roles() fails with a NULL password");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ ok (mongo_sync_cmd_user_add_with_roles (c, "test",
+ "test", "s3kr1+", NULL) == FALSE,
+ "mongo_sync_cmd_user_add() fails with a bogus FD and empty roles");
+
+ ok (mongo_sync_cmd_user_add_with_roles (c, "test",
+ "test", "s3kr1+", roles) == FALSE,
+ "mongo_sync_cmd_user_add() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_user_add_with_roles_net (roles);
+
+ bson_free (roles);
+}
+
+RUN_TEST (12, mongo_sync_cmd_user_add_with_roles);
diff --git a/tests/unit/mongo/sync/sync_cmd_user_remove.c b/tests/unit/mongo/sync/sync_cmd_user_remove.c
new file mode 100644
index 0000000..dc66063
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_cmd_user_remove.c
@@ -0,0 +1,92 @@
+#include "test.h"
+#include "mongo.h"
+#include "config.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_cmd_user_remove_net_secondary (void)
+{
+ mongo_sync_connection *c;
+ gboolean ret;
+
+ skip (!config.secondary_host, 1,
+ "Secondary server not configured");
+
+ c = mongo_sync_connect (config.secondary_host, config.secondary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ mongo_sync_cmd_user_add (c, config.db, "test", "s3kr1+");
+ ret = mongo_sync_cmd_user_remove (c, config.db, "test");
+ ok (ret && mongo_sync_cmd_is_master (c),
+ "mongo_sync_cmd_user_remove() automatically reconnects to master");
+
+ mongo_sync_disconnect (c);
+
+ endskip;
+}
+
+void
+test_mongo_sync_cmd_user_remove_net (void)
+{
+ mongo_sync_connection *c;
+
+ begin_network_tests (3);
+
+ c = mongo_sync_connect (config.primary_host, config.primary_port, TRUE);
+ mongo_sync_conn_set_auto_reconnect (c, TRUE);
+
+ mongo_sync_cmd_user_add (c, config.db, "test", "s3kr1+");
+ ok (mongo_sync_cmd_user_remove (c, config.db, "test") == TRUE,
+ "mongo_sync_cmd_user_remove() works");
+
+ mongo_sync_cmd_user_add (c, config.db, "test", "s3kr1+");
+ shutdown (c->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok (mongo_sync_cmd_user_remove (c, config.db, "test") == TRUE,
+ "mongo_sync_cmd_user_remove() automatically reconnects");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_user_remove_net_secondary ();
+
+ end_network_tests ();
+}
+
+void
+test_mongo_sync_cmd_user_remove (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_remove (NULL, "test", "test") == FALSE,
+ "mongo_sync_cmd_user_remove() fails with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN");
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_remove (c, NULL, "test") == FALSE,
+ "mongo_sync_cmd_user_remove() fails with a NULL db");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ errno = 0;
+ ok (mongo_sync_cmd_user_remove (c, "test", NULL) == FALSE,
+ "mongo_sync_cmd_user_remove() fails with a NULL user");
+ cmp_ok (errno, "==", EINVAL,
+ "errno is set to EINVAL");
+
+ ok (mongo_sync_cmd_user_remove (c, "test", "test") == FALSE,
+ "mongo_sync_cmd_user_remove() fails with a bogus FD");
+
+ mongo_sync_disconnect (c);
+
+ test_mongo_sync_cmd_user_remove_net ();
+}
+
+RUN_TEST (10, mongo_sync_cmd_user_remove);
diff --git a/tests/unit/mongo/sync/sync_conn_seed_add.c b/tests/unit/mongo/sync/sync_conn_seed_add.c
new file mode 100644
index 0000000..fb9f10a
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_conn_seed_add.c
@@ -0,0 +1,24 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_conn_seed_add (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (42, TRUE);
+
+ ok (mongo_sync_conn_seed_add (NULL, "localhost", 27017) == FALSE,
+ "mongo_sync_conn_seed_add() should fail with a NULL connection");
+ ok (mongo_sync_conn_seed_add (c, NULL, 27017) == FALSE,
+ "mongo_sync_conn_seed_add() should fail with a NULL host");
+ ok (mongo_sync_conn_seed_add (c, "localhost", -1) == FALSE,
+ "mongo_sync_conn_seed_add() should fail with an invalid port");
+
+ ok (mongo_sync_conn_seed_add (c, "localhost", 27017),
+ "mongo_sync_conn_seed_add() works");
+
+ mongo_sync_disconnect (c);
+}
+
+RUN_TEST (4, mongo_sync_conn_seed_add);
diff --git a/tests/unit/mongo/sync/sync_conn_seed_add_cache.c b/tests/unit/mongo/sync/sync_conn_seed_add_cache.c
new file mode 100644
index 0000000..e049691
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_conn_seed_add_cache.c
@@ -0,0 +1,31 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_connection_cache_seed_add (void)
+{
+ mongo_sync_conn_recovery_cache *cache;
+
+ cache = mongo_sync_conn_recovery_cache_new ();
+
+ ok (mongo_sync_conn_recovery_cache_seed_add (cache,
+ "localhost",
+ 27017) == TRUE,
+ "mongo_sync_connection_cache_seed_add() works");
+
+ ok (mongo_sync_conn_recovery_cache_seed_add (cache,
+ NULL,
+ 27017) == FALSE,
+ "mongo_sync_connection_cache_seed_add() should fail with a NULL host");
+
+ mongo_sync_conn_recovery_cache_discard (cache);
+
+ ok (mongo_sync_conn_recovery_cache_seed_add (cache,
+ "localhost",
+ 27017) == TRUE,
+ "mongo_sync_connection_cache_seed_add() works");
+
+ mongo_sync_conn_recovery_cache_free (cache);
+}
+
+RUN_TEST (3, mongo_sync_connection_cache_seed_add);
diff --git a/tests/unit/mongo/sync/sync_connect.c b/tests/unit/mongo/sync/sync_connect.c
new file mode 100644
index 0000000..418c2bf
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_connect.c
@@ -0,0 +1,22 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_connect (void)
+{
+ mongo_sync_connection *c;
+
+ ok (mongo_sync_connect (NULL, 27017, FALSE) == NULL,
+ "mongo_sync_connect() fails with a NULL host");
+
+ begin_network_tests (1);
+
+ ok ((c = mongo_sync_connect (config.primary_host,
+ config.primary_port, FALSE)) != NULL,
+ "mongo_sync_connect() works");
+ mongo_sync_disconnect (c);
+
+ end_network_tests ();
+}
+
+RUN_TEST (2, mongo_sync_connect);
diff --git a/tests/unit/mongo/sync/sync_connect_cache.c b/tests/unit/mongo/sync/sync_connect_cache.c
new file mode 100644
index 0000000..1618899
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_connect_cache.c
@@ -0,0 +1,42 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_sync_conn_recovery_cache_connection (void)
+{
+ mongo_sync_conn_recovery_cache *cache;
+ mongo_sync_connection *c = NULL;
+
+ cache = mongo_sync_conn_recovery_cache_new ();
+
+ ok (mongo_sync_connect_recovery_cache (cache, FALSE) == NULL,
+ "mongo_sync_connect_recovery_cache() should fail when cache is empty");
+
+ begin_network_tests (4);
+
+ ok (mongo_sync_conn_recovery_cache_seed_add (cache,
+ config.primary_host,
+ config.primary_port) == TRUE,
+ "mongo_sync_conn_recovery_cache_seed_add() works");
+
+ ok ((c = mongo_sync_connect_recovery_cache (cache, FALSE)) != NULL,
+ "mongo_sync_connect_recovery_cache() works");
+
+ mongo_sync_disconnect (c);
+
+ ok ((c = mongo_sync_connect_recovery_cache (cache, FALSE)) != NULL,
+ "mongo_sync_connect_recovery_cache() works after disconnect");
+
+ mongo_sync_disconnect (c);
+
+ mongo_sync_conn_recovery_cache_discard (cache);
+
+ ok (mongo_sync_connect_recovery_cache (cache, TRUE) == NULL,
+ "mongo_sync_connect_recovery_cache() should fail when cache is discarded");
+
+ mongo_sync_conn_recovery_cache_free (cache);
+
+ end_network_tests ();
+}
+
+RUN_TEST (5, mongo_sync_conn_recovery_cache_connection);
diff --git a/tests/unit/mongo/sync/sync_connect_from_cache_enforce_primary.c b/tests/unit/mongo/sync/sync_connect_from_cache_enforce_primary.c
new file mode 100644
index 0000000..5c48ae9
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_connect_from_cache_enforce_primary.c
@@ -0,0 +1,47 @@
+#include "test.h"
+#include "mongo.h"
+
+#define NETWORK_TESTS_NUM 5
+
+void
+test_mongo_sync_connect_from_cache_enforce_primary (void)
+{
+ mongo_sync_conn_recovery_cache *cache;
+ mongo_sync_connection *c;
+
+ begin_network_tests (NETWORK_TESTS_NUM);
+
+ cache = mongo_sync_conn_recovery_cache_new ();
+
+ skip (!config.secondary_host,
+ NETWORK_TESTS_NUM,
+ "Secondary server not configured");
+
+ ok (mongo_sync_conn_recovery_cache_seed_add (cache, config.secondary_host,
+ config.secondary_port) == TRUE,
+ "mongo_sync_conn_recovery_seed_add() works");
+
+ ok ((c = mongo_sync_connect_recovery_cache (cache, TRUE)) != NULL,
+ "mongo_sync_connect_recovery_cache() works");
+
+ ok (mongo_sync_cmd_is_master(c) == FALSE,
+ "Secondary server should not be The Master.");
+
+ mongo_sync_disconnect (c);
+
+ ok ((c = mongo_sync_connect_recovery_cache (cache, FALSE)) != NULL,
+ "mongo_sync_connect_recovery_cache() works");
+
+ ok (mongo_sync_cmd_is_master (c) == TRUE,\
+ "Retrieved connection should be The Master when it is forced to be.");
+
+ mongo_sync_disconnect (c);
+
+ endskip;
+
+ mongo_sync_conn_recovery_cache_free (cache);
+
+ end_network_tests ();
+}
+
+RUN_TEST (NETWORK_TESTS_NUM, mongo_sync_connect_from_cache_enforce_primary);
diff --git a/tests/unit/mongo/sync/sync_disconnect.c b/tests/unit/mongo/sync/sync_disconnect.c
new file mode 100644
index 0000000..f7783e7
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_disconnect.c
@@ -0,0 +1,22 @@
+#include "test.h"
+#include "mongo.h"
+
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_disconnect (void)
+{
+ mongo_sync_connection *conn;
+
+ mongo_sync_disconnect (NULL);
+ pass ("mongo_sync_disconnect(NULL) does not crash");
+
+ conn = test_make_fake_sync_conn (-1, FALSE);
+ conn->rs.hosts = g_list_append (conn->rs.hosts,
+ g_strdup ("invalid.example.com:-42"));
+
+ mongo_sync_disconnect (conn);
+ pass ("mongo_sync_disconnect() works");
+}
+
+RUN_TEST (2, mongo_sync_disconnect);
diff --git a/tests/unit/mongo/sync/sync_get_set_auto_reconnect.c b/tests/unit/mongo/sync/sync_get_set_auto_reconnect.c
new file mode 100644
index 0000000..bfe2719
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_get_set_auto_reconnect.c
@@ -0,0 +1,39 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_get_set_auto_reconnect (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_conn_get_auto_reconnect (NULL) == FALSE,
+ "mongo_sync_conn_get_auto_reconnect() returns FALSE with a "
+ "NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is now set to ENOTCONN");
+
+ ok (mongo_sync_conn_get_auto_reconnect (c) == FALSE,
+ "mongo_sync_get_auto_reconnect() works");
+ cmp_ok (errno, "==", 0,
+ "errno is now cleared");
+
+ errno = 0;
+ mongo_sync_conn_set_auto_reconnect (NULL, TRUE);
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN after "
+ "mongo_sync_conn_set_auto_reconnect(NULL)");
+
+ ok (mongo_sync_conn_set_auto_reconnect (c, TRUE),
+ "mongo_sync_auto_reconnect() works");
+ ok (mongo_sync_conn_get_auto_reconnect (c) == TRUE,
+ "mongo_sync_set_auto_reconnect() worked");
+
+ mongo_sync_disconnect (c);
+}
+
+RUN_TEST (7, mongo_sync_get_set_auto_reconnect);
diff --git a/tests/unit/mongo/sync/sync_get_set_max_insert_size.c b/tests/unit/mongo/sync/sync_get_set_max_insert_size.c
new file mode 100644
index 0000000..51970a3
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_get_set_max_insert_size.c
@@ -0,0 +1,44 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_get_set_max_insert_size (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_conn_get_max_insert_size (NULL) == -1,
+ "mongo_sync_conn_get_max_insert_size() returns -1 with "
+ "a NULL connection");
+
+ cmp_ok (mongo_sync_conn_get_max_insert_size (c), "==",
+ MONGO_SYNC_DEFAULT_MAX_INSERT_SIZE,
+ "mongo_sync_get_max_insert_size() works");
+
+ errno = 0;
+ mongo_sync_conn_set_max_insert_size (NULL, 1024);
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN after "
+ "mongo_sync_conn_set_max_insert_size(NULL)");
+
+ mongo_sync_conn_set_max_insert_size (c, 1024);
+ cmp_ok (errno, "==", 0,
+ "errno is cleared");
+ ok (mongo_sync_conn_get_max_insert_size (c) == 1024,
+ "mongo_sync_set_max_insert_size() worked");
+
+ mongo_sync_conn_set_max_insert_size (c, -1);
+ cmp_ok (errno, "==", ERANGE,
+ "errno is set to ERANGE");
+ ok (mongo_sync_conn_get_max_insert_size (c) == 1024,
+ "mongo_sync_set_max_insert_size() with a negative value should "
+ "not work");
+
+ mongo_sync_disconnect (c);
+}
+
+RUN_TEST (7, mongo_sync_get_set_max_insert_size);
diff --git a/tests/unit/mongo/sync/sync_get_set_safe_mode.c b/tests/unit/mongo/sync/sync_get_set_safe_mode.c
new file mode 100644
index 0000000..b444105
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_get_set_safe_mode.c
@@ -0,0 +1,38 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_get_set_safe_mode (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_conn_get_safe_mode (NULL) == FALSE,
+ "mongo_sync_conn_get_safe_mode() returns FALSE with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is now set to ENOTCONN");
+
+ ok (mongo_sync_conn_get_safe_mode (c) == FALSE,
+ "mongo_sync_get_safe_mode() works");
+ cmp_ok (errno, "==", 0,
+ "errno is now cleared");
+
+ errno = 0;
+ mongo_sync_conn_set_safe_mode (NULL, TRUE);
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN after mongo_sync_conn_get_safe_mode(NULL)");
+
+ mongo_sync_conn_set_safe_mode (c, TRUE);
+ cmp_ok (errno, "==", 0,
+ "errno is cleared");
+ ok (mongo_sync_conn_get_safe_mode (c) == TRUE,
+ "mongo_sync_set_safe_mode() worked");
+
+ mongo_sync_disconnect (c);
+}
+
+RUN_TEST (7, mongo_sync_get_set_safe_mode);
diff --git a/tests/unit/mongo/sync/sync_get_set_slaveok.c b/tests/unit/mongo/sync/sync_get_set_slaveok.c
new file mode 100644
index 0000000..7a43979
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_get_set_slaveok.c
@@ -0,0 +1,38 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+
+void
+test_mongo_sync_get_set_slaveok (void)
+{
+ mongo_sync_connection *c;
+
+ c = test_make_fake_sync_conn (-1, FALSE);
+
+ errno = 0;
+ ok (mongo_sync_conn_get_slaveok (NULL) == FALSE,
+ "mongo_sync_conn_get_slaveok() returns FALSE with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is now set to ENOTCONN");
+
+ ok (mongo_sync_conn_get_slaveok (c) == FALSE,
+ "mongo_sync_get_slaveok() works");
+ cmp_ok (errno, "==", 0,
+ "errno is now cleared");
+
+ errno = 0;
+ mongo_sync_conn_set_slaveok (NULL, TRUE);
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is set to ENOTCONN after mongo_sync_conn_get_slaveok(NULL)");
+
+ mongo_sync_conn_set_slaveok (c, TRUE);
+ cmp_ok (errno, "==", 0,
+ "errno is cleared");
+ ok (mongo_sync_conn_get_slaveok (c) == TRUE,
+ "mongo_sync_set_slaveok() worked");
+
+ mongo_sync_disconnect (c);
+}
+
+RUN_TEST (7, mongo_sync_get_set_slaveok);
diff --git a/tests/unit/mongo/sync/sync_reconnect.c b/tests/unit/mongo/sync/sync_reconnect.c
new file mode 100644
index 0000000..a81e4da
--- /dev/null
+++ b/tests/unit/mongo/sync/sync_reconnect.c
@@ -0,0 +1,143 @@
+#include "test.h"
+#include "mongo.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include "libmongo-private.h"
+
+void
+test_mongo_sync_reconnect (void)
+{
+ mongo_sync_connection *conn, *o;
+ GList *l;
+
+ ok (mongo_sync_reconnect (NULL, FALSE) == NULL,
+ "mongo_sync_reconnect() fails with a NULL connection");
+ cmp_ok (errno, "==", ENOTCONN,
+ "errno is ENOTCONN");
+
+ conn = test_make_fake_sync_conn (-1, FALSE);
+ ok (mongo_sync_reconnect (conn, FALSE) == NULL,
+ "mongo_sync_reconnect() fails with a bogus FD");
+ cmp_ok (errno, "==", EHOSTUNREACH,
+ "errno is EHOSTUNREACH");
+
+ mongo_sync_disconnect (conn);
+
+ begin_network_tests (15);
+
+ /* Connect & reconnect to master */
+ o = conn = mongo_sync_connect (config.primary_host,
+ config.primary_port, TRUE);
+ ok ((conn = mongo_sync_reconnect (conn, TRUE)) != NULL,
+ "mongo_sync_reconnect() works when reconnecting to self");
+ ok (o == conn,
+ "Reconnect to an existing master results in the same object");
+ mongo_sync_disconnect (conn);
+
+ /* Connect to master, kill FD, reconnect */
+ conn = mongo_sync_connect (config.primary_host,
+ config.primary_port, TRUE);
+ mongo_sync_cmd_is_master (conn);
+
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+
+ ok ((conn = mongo_sync_reconnect (conn, TRUE)) != NULL,
+ "mongo_sync_reconnect() succeed when the connection drops");
+ mongo_sync_disconnect (conn);
+
+ /* Connect, kill, reconnect; w/o knowing other hosts */
+ o = conn = mongo_sync_connect (config.primary_host,
+ config.primary_port, TRUE);
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+ l = conn->rs.hosts;
+ while (l)
+ {
+ g_free (l->data);
+ l = g_list_delete_link (l, l);
+ }
+ conn->rs.hosts = NULL;
+
+ l = conn->rs.seeds;
+ while (l)
+ {
+ g_free (l->data);
+ l = g_list_delete_link (l, l);
+ }
+ conn->rs.seeds = NULL;
+
+ conn = mongo_sync_reconnect (conn, FALSE);
+
+ ok (conn != o && conn == NULL,
+ "mongo_sync_reconnect() fails if it can't reconnect anywhere");
+ mongo_sync_disconnect (o);
+
+ /* Gracefully ignore unparsable hosts during reconnect */
+ o = conn = mongo_sync_connect (config.primary_host,
+ config.primary_port, TRUE);
+ mongo_sync_cmd_is_master (conn);
+ conn->rs.hosts = g_list_prepend (conn->rs.hosts,
+ g_strdup ("invalid:-42"));
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+ conn = mongo_sync_reconnect (conn, TRUE);
+
+ ok (conn == o,
+ "mongo_sync_reconnect() gracefully ignores unparsable hosts "
+ "during reconnect");
+ mongo_sync_disconnect (conn);
+
+ /* Ignore unreachable hosts during reconnect */
+ o = conn = mongo_sync_connect (config.primary_host,
+ config.primary_port, TRUE);
+ mongo_sync_cmd_is_master (conn);
+ conn->rs.hosts = g_list_prepend (conn->rs.hosts,
+ g_strdup ("example.com:27017"));
+ shutdown (conn->super.fd, SHUT_RDWR);
+ sleep (3);
+ conn = mongo_sync_reconnect (conn, TRUE);
+
+ ok (conn == o,
+ "mongo_sync_reconnect() gracefully ignores unparsable hosts "
+ "during reconnect");
+ mongo_sync_disconnect (conn);
+
+ /*
+ * Tests involving a secondary
+ */
+
+ skip (!config.secondary_host, 9,
+ "Secondary host not set up");
+
+ /* Connect to secondary & reconnect to master */
+ o = conn = mongo_sync_connect (config.secondary_host,
+ config.secondary_port, TRUE);
+ ok (conn != NULL, "Connecting to secondary");
+ ok (mongo_sync_cmd_is_master (conn) == FALSE,
+ "Connected to a secondary");
+ ok ((conn = mongo_sync_reconnect (conn, TRUE)) != NULL,
+ "Reconnecting from slave to master succeeds");
+ ok (conn == o, "Connection object updated in-place");
+ ok (mongo_sync_cmd_is_master (conn),
+ "Correctly reconnected to master");
+ mongo_sync_disconnect (conn);
+
+ /* Connect to secondary & reconnect to self */
+ o = conn = mongo_sync_connect (config.secondary_host,
+ config.secondary_port, TRUE);
+ ok (conn != NULL, "Connecting to secondary");
+ ok ((conn = mongo_sync_reconnect (conn, FALSE)) != NULL,
+ "Reconnecting from slave to self succeeds");
+ ok (conn == o, "Connection object updated in-place");
+ ok (mongo_sync_cmd_is_master (conn) == FALSE,
+ "Correctly reconnected to self");
+ mongo_sync_disconnect (conn);
+
+ endskip;
+
+ end_network_tests ();
+}
+
+RUN_TEST (19, mongo_sync_reconnect);
diff --git a/tests/unit/mongo/utils/oid_as_string.c b/tests/unit/mongo/utils/oid_as_string.c
new file mode 100644
index 0000000..9cf740c
--- /dev/null
+++ b/tests/unit/mongo/utils/oid_as_string.c
@@ -0,0 +1,26 @@
+#include "test.h"
+#include "mongo.h"
+
+void
+test_mongo_utils_oid_as_string (void)
+{
+ guint8 *oid;
+ gchar *oid_str;
+
+ mongo_util_oid_init (0);
+
+ oid = mongo_util_oid_new (1);
+
+ ok (mongo_util_oid_as_string (NULL) == NULL,
+ "mongo_util_oid_as_string() should fail with a NULL oid");
+
+ oid_str = mongo_util_oid_as_string (oid);
+
+ ok (oid_str != NULL,
+ "mongo_util_oid_as_string() works");
+
+ g_free (oid_str);
+ g_free (oid);
+}
+
+RUN_TEST (2, mongo_utils_oid_as_string);
diff --git a/tests/unit/mongo/utils/oid_init.c b/tests/unit/mongo/utils/oid_init.c
new file mode 100644
index 0000000..42d0db1
--- /dev/null
+++ b/tests/unit/mongo/utils/oid_init.c
@@ -0,0 +1,19 @@
+#include "tap.h"
+#include "test.h"
+#include "mongo-utils.h"
+
+void
+test_mongo_utils_oid_init (void)
+{
+ mongo_util_oid_init (0);
+ mongo_util_oid_init (1234);
+
+ /* We don't do any real testing here, only check if it does not
+ crash. To verify that it works, we need to create a new OID, and
+ that will be tested by other unit tests.
+ */
+ ok (TRUE,
+ "mongo_util_oid_init() does not crash.");
+}
+
+RUN_TEST (1, mongo_utils_oid_init);
diff --git a/tests/unit/mongo/utils/oid_new.c b/tests/unit/mongo/utils/oid_new.c
new file mode 100644
index 0000000..b8f7f0a
--- /dev/null
+++ b/tests/unit/mongo/utils/oid_new.c
@@ -0,0 +1,49 @@
+#include "tap.h"
+#include "test.h"
+#include "mongo-utils.h"
+
+#include <string.h>
+#include <unistd.h>
+
+void
+test_mongo_utils_oid_new (void)
+{
+ guint8 *oid1, *oid2, *oid3;
+ gchar *oid1_s, *oid2_s;
+
+ ok (mongo_util_oid_new (0) == NULL,
+ "mongo_util_oid_new() should fail before mongo_util_oid_init()");
+
+ mongo_util_oid_init (0);
+ ok ((oid1 = mongo_util_oid_new (1)) != NULL,
+ "mongo_util_oid_new() works");
+ cmp_ok (oid1[11], "==", 1,
+ "mongo_util_oid_new() returns an OID with the currect seq ID");
+
+ oid2 = mongo_util_oid_new (2);
+ oid3 = mongo_util_oid_new (2);
+
+ ok (memcmp (oid2, oid1, 12) > 0,
+ "OIDs with higher sequence ID sort higher");
+ ok (memcmp (oid2, oid3, 12) == 0,
+ "OIDs with the same sequence ID are equal (within a second)");
+ g_free (oid2);
+ g_free (oid3);
+
+ sleep (2);
+ oid2 = mongo_util_oid_new (0);
+
+ oid1_s = mongo_util_oid_as_string (oid1);
+ oid2_s = mongo_util_oid_as_string (oid2);
+
+ ok (memcmp (oid2, oid1, 12) > 0,
+ "OIDs with the same sequence ID, a few seconds later sort higher; "
+ "oid1=%s; oid2=%s", oid1_s, oid2_s);
+
+ g_free (oid2_s);
+ g_free (oid1_s);
+ g_free (oid2);
+ g_free (oid1);
+}
+
+RUN_TEST (6, mongo_utils_oid_new);
diff --git a/tests/unit/mongo/utils/oid_new_with_time.c b/tests/unit/mongo/utils/oid_new_with_time.c
new file mode 100644
index 0000000..290fdab
--- /dev/null
+++ b/tests/unit/mongo/utils/oid_new_with_time.c
@@ -0,0 +1,46 @@
+#include "tap.h"
+#include "test.h"
+#include "mongo-utils.h"
+
+#include <string.h>
+
+void
+test_mongo_utils_oid_new_with_time (void)
+{
+ guint8 *oid1, *oid2, *oid3;
+ gchar *oid1_s, *oid2_s;
+
+ ok (mongo_util_oid_new_with_time (0, 0) == NULL,
+ "mongo_util_oid_new_with_time() should fail before mongo_util_oid_init()");
+
+ mongo_util_oid_init (0);
+ ok ((oid1 = mongo_util_oid_new_with_time (0, 1)) != NULL,
+ "mongo_util_oid_new_with_time() works");
+ cmp_ok (oid1[11], "==", 1,
+ "mongo_util_oid_new_with_time() returns an OID with the currect seq ID");
+
+ oid2 = mongo_util_oid_new_with_time (0, 2);
+ oid3 = mongo_util_oid_new_with_time (0, 2);
+
+ ok (memcmp (oid2, oid1, 12) > 0,
+ "OIDs with higher sequence ID sort higher");
+ ok (memcmp (oid2, oid3, 12) == 0,
+ "OIDs with the same sequence ID are equal (within a second)");
+ g_free (oid2);
+ g_free (oid3);
+
+ oid2 = mongo_util_oid_new_with_time (1, 0);
+
+ oid1_s = mongo_util_oid_as_string (oid1);
+ oid2_s = mongo_util_oid_as_string (oid2);
+
+ ok (memcmp (oid2, oid1, 12) > 0,
+ "OIDs with the same sequence ID, a few seconds later sort higher; "
+ "oid1=%s; oid2=%s", oid1_s, oid2_s);
+ g_free (oid2_s);
+ g_free (oid1_s);
+ g_free (oid2);
+ g_free (oid1);
+}
+
+RUN_TEST (6, mongo_utils_oid_new_with_time);
diff --git a/tests/unit/mongo/utils/parse_addr.c b/tests/unit/mongo/utils/parse_addr.c
new file mode 100644
index 0000000..13b16d1
--- /dev/null
+++ b/tests/unit/mongo/utils/parse_addr.c
@@ -0,0 +1,244 @@
+#include "tap.h"
+#include "test.h"
+#include "mongo-utils.h"
+
+#include <string.h>
+
+void
+test_mongo_utils_parse_addr (void)
+{
+ gchar *host = "deadbeef";
+ gint port = 42;
+
+ ok (mongo_util_parse_addr (NULL, &host, &port) == FALSE,
+ "mongo_util_parse_addr() fails with a NULL address");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("127.0.0.1:27017", &host, NULL) == FALSE,
+ "mongo_util_parse_addr() fails when port is NULL");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("127.0.0.1:27017", NULL, &port) == FALSE,
+ "mongo_util_parse_addr() fails when host is NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("127.0.0.1:27017", &host, &port),
+ "mongo_util_parse_addr() can parse HOST:PORT pairs");
+ is (host, "127.0.0.1",
+ "Host parsed successfully");
+ cmp_ok (port, "==", 27017,
+ "Port parsed successfully");
+ g_free (host);
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr (":27017", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail when no host is specified");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("localhost:27017garbage", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if there is garbage after "
+ "the port");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("localhost:garbage", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if the port is not a number");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("localhost:-10", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if the port is out of bounds");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("localhost:9999999999999999999",
+ &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if the port is out of bounds");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("localhost:9999999999",
+ &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if the port is out of bounds");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ /* IPv6 */
+ ok (mongo_util_parse_addr ("::1:27017", &host, &port),
+ "mongo_util_parse_addr() can deal with IPv6 addresses");
+ is (host, "::1",
+ "Host parsed successfully");
+ cmp_ok (port, "==", 27017,
+ "Port parsed successfully");
+ g_free (host);
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("::1", &host, &port),
+ "mongo_util_parse_addr() should silently misparse ambigous "
+ "IPv6 addresses");
+ isnt (host, "::1",
+ "Host is misparsed, as expected");
+ cmp_ok (port, "==", 1,
+ "Port is misparsed, as expected");
+ g_free (host);
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail on invalid IPv6 literals");
+ is (host, NULL,
+ "Host should be NULL");
+ cmp_ok (port, "==", -1,
+ "Port should be -1");
+ g_free (host);
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1]:1", &host, &port),
+ "mongo_util_parse_addr() works with IPv6 literal + port");
+ is (host, "::1",
+ "Host should be ::1");
+ cmp_ok (port, "==", 1,
+ "Port should be 1");
+ g_free (host);
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1]:27017", &host, &port),
+ "mongo_util_parse_addr() works with IPv6 literal + port");
+ is (host, "::1",
+ "Host should be ::1");
+ cmp_ok (port, "==", 27017,
+ "Port should be 27017");
+ g_free (host);
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[]:27017", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail when no host is specified");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1]:27017garbage", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if there is garbage after "
+ "the port");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1]:garbage", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if the port is not a number");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1]:-10", &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if the port is out of bounds");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1]:9999999999999999999",
+ &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if the port is out of bounds");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1]:9999999999",
+ &host, &port) == FALSE,
+ "mongo_util_parse_addr() should fail if the port is out of bounds");
+ is (host, NULL,
+ "Failed parsing sets host to NULL");
+ cmp_ok (port, "==", -1,
+ "Failed parsing sets port to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("/var/run/mongodb/mongodb.socket",
+ &host, &port) == TRUE,
+ "mongo_util_parse_addr() works with unix domain sockets");
+ is (host, "/var/run/mongodb/mongodb.socket",
+ "Parsing a Unix domain socket sets host to the socket name");
+ cmp_ok (port, "==", -1,
+ "Port is set to -1");
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("[::1]", &host, &port),
+ "mongo_util_parse_addr() can handle IPv6 literals without port set");
+ is (host, "::1",
+ "Host parsed successfully");
+ cmp_ok (port, "==", -1,
+ "Port is set to -1");
+ g_free (host);
+ host = "deadbeef";
+ port = 42;
+
+ ok (mongo_util_parse_addr ("/var/run/mongodb/mongodb.socket:-1",
+ &host, &port) == TRUE,
+ "mongo_util_parse_addr() can parse unix domain sockets with -1 port");
+ is (host, "/var/run/mongodb/mongodb.socket",
+ "Parsing a unix domain socket sets host to the socket name");
+ cmp_ok (port, "==", -1,
+ "Parsing a unix domain socket with a port set to -1, works");
+ g_free (host);
+ host = "deadbeef";
+ port = 42;
+}
+
+RUN_TEST (70, mongo_utils_parse_addr);
diff --git a/tests/unit/mongo/wire/cmd_custom.c b/tests/unit/mongo/wire/cmd_custom.c
new file mode 100644
index 0000000..7970aaa
--- /dev/null
+++ b/tests/unit/mongo/wire/cmd_custom.c
@@ -0,0 +1,67 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_cmd_custom (void)
+{
+ bson *cmd;
+ mongo_packet *p;
+
+ mongo_packet_header hdr;
+ const guint8 *data;
+ gint32 data_size;
+
+ bson_cursor *c;
+ gint32 pos;
+
+ cmd = bson_new ();
+ bson_append_int32 (cmd, "getnonce", 1);
+
+ ok (mongo_wire_cmd_custom (1, "test", 0, NULL) == NULL,
+ "mongo_wire_cmd_custom() fails with a NULL command");
+ ok (mongo_wire_cmd_custom (1, "test", 0, cmd) == NULL,
+ "mongo_wire_cmd_custom() fails with an unfinished command");
+ bson_finish (cmd);
+ ok (mongo_wire_cmd_custom (1, NULL, 0, cmd) == NULL,
+ "mongo_wire_cmd_custom() fails with a NULL db");
+
+ ok ((p = mongo_wire_cmd_custom (1, "test", 0, cmd)) != NULL,
+ "mongo_wire_cmd_custom() works");
+ bson_free (cmd);
+
+ /* Verify the header */
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size looks fine");
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is OK");
+ cmp_ok (hdr.id, "==", 1, "Packet request ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Packet reply ID is ok");
+
+ /*
+ * Test the created request
+ */
+
+ /* pos = zero + collection_name + NULL + skip + ret */
+ pos = sizeof (gint32) + strlen ("test.$cmd") + 1 + sizeof (gint32) * 2;
+ ok ((cmd = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "Packet contains a BSON document");
+ bson_finish (cmd);
+
+ ok ((c = bson_find (cmd, "getnonce")) != NULL,
+ "BSON object contains a 'getnonce' key");
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT32,
+ "'getnonce' key has the correct type");
+ ok (bson_cursor_next (c) == FALSE,
+ "'getnonce' key is the last in the object");
+
+ bson_cursor_free (c);
+ bson_free (cmd);
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (12, mongo_wire_cmd_custom);
diff --git a/tests/unit/mongo/wire/cmd_delete.c b/tests/unit/mongo/wire/cmd_delete.c
new file mode 100644
index 0000000..9399046
--- /dev/null
+++ b/tests/unit/mongo/wire/cmd_delete.c
@@ -0,0 +1,73 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_cmd_delete (void)
+{
+ mongo_packet *p;
+ bson *s, *tmp;
+
+ mongo_packet_header hdr;
+ const guint8 *data;
+ gint32 data_size;
+
+ gint32 pos;
+ bson_cursor *c;
+
+ s = test_bson_generate_full ();
+ tmp = bson_new ();
+
+ ok (mongo_wire_cmd_delete (1, NULL, 0, s) == NULL,
+ "mongo_wire_cmd_delete() fails with a NULL namespace");
+ ok (mongo_wire_cmd_delete (1, "test.ns", 0, NULL) == NULL,
+ "mongo_wire_cmd_delete() fails with a NULL selector");
+ ok (mongo_wire_cmd_delete (1, "test.ns", 0, tmp) == NULL,
+ "mongo_wire_cmd_delete() fails with an unfinished selector");
+ bson_free (tmp);
+
+ ok ((p = mongo_wire_cmd_delete (1, "test.ns", 0, s)) != NULL,
+ "mongo_wire_cmd_delete() works");
+ bson_free (s);
+
+ /* Test basic header data */
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size appears fine");
+
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is correct");
+ cmp_ok (hdr.id, "==", 1, "Header ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Response ID is ok");
+
+ /*
+ * Test the constructed request
+ */
+
+ /* pos = zero + ns + NULL + flags */
+ pos = sizeof (gint32) + strlen ("test.ns") + 1 + sizeof (gint32);
+
+ ok ((s = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "Packet contains a valid BSON update document");
+ bson_finish (s);
+
+ ok ((c = bson_find (s, "int32")) != NULL,
+ "BSON contains 'int32'");
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT32,
+ "int32 has correct type");
+ bson_cursor_next (c);
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT64,
+ "next element has correct type too");
+ ok (bson_cursor_next (c) == FALSE,
+ "No more data after the update BSON object");
+
+ bson_cursor_free (c);
+ bson_free (s);
+
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (13, mongo_wire_cmd_delete);
diff --git a/tests/unit/mongo/wire/cmd_get_more.c b/tests/unit/mongo/wire/cmd_get_more.c
new file mode 100644
index 0000000..5f56821
--- /dev/null
+++ b/tests/unit/mongo/wire/cmd_get_more.c
@@ -0,0 +1,50 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_cmd_get_more (void)
+{
+ mongo_packet *p;
+
+ mongo_packet_header hdr;
+ const guint8 *data;
+ gint32 data_size;
+
+ gint32 pos;
+ gint64 cid = 9876543210;
+
+ ok (mongo_wire_cmd_get_more (1, NULL, 1, cid) == NULL,
+ "mongo_wire_cmd_get_more() fails with a NULL namespace");
+ ok ((p = mongo_wire_cmd_get_more (1, "test.ns", 1, cid)) != NULL,
+ "mongo_wire_cmd_get_more() works");
+
+ /* Test basic header data */
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size appears fine");
+
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is correct");
+ cmp_ok (hdr.id, "==", 1, "Header ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Response ID is ok");
+
+ /*
+ * Test the request itself.
+ */
+
+ /* pos = zero + ns + NULL + ret */
+ pos = sizeof (gint32) + strlen ("test.ns") + 1 + sizeof (gint32);
+ cid = 0;
+ memcpy (&cid, data + pos, sizeof (cid));
+ cid = GINT64_FROM_LE (cid);
+
+ ok (cid == 9876543210,
+ "Included CID is correct");
+
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (7, mongo_wire_cmd_get_more);
diff --git a/tests/unit/mongo/wire/cmd_insert.c b/tests/unit/mongo/wire/cmd_insert.c
new file mode 100644
index 0000000..3e84847
--- /dev/null
+++ b/tests/unit/mongo/wire/cmd_insert.c
@@ -0,0 +1,83 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_cmd_insert (void)
+{
+ bson *ins, *tmp;
+ mongo_packet *p;
+
+ mongo_packet_header hdr;
+ const guint8 *data;
+ gint32 data_size;
+
+ bson_cursor *c;
+ gint32 pos;
+
+ ins = test_bson_generate_full ();
+ tmp = bson_new ();
+
+ ok (mongo_wire_cmd_insert (1, NULL, ins, NULL) == NULL,
+ "mongo_wire_cmd_insert() fails with a NULL namespace");
+ ok (mongo_wire_cmd_insert (1, "test.ns", NULL) == NULL,
+ "mongo_wire_cmd_insert() fails with no documents");
+ ok (mongo_wire_cmd_insert (1, "test.ns", tmp, NULL) == NULL,
+ "mongo_wire_cmd_insert() with an unfinished document");
+ bson_finish (tmp);
+ ok ((p = mongo_wire_cmd_insert (1, "test.ns", ins, tmp, NULL)) != NULL,
+ "mongo_wire_cmd_insert() works");
+ bson_free (ins);
+ bson_free (tmp);
+
+ /* Test basic header data */
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size appears fine");
+
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is correct");
+ cmp_ok (hdr.id, "==", 1, "Header ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Response ID is ok");
+
+ /*
+ * Test the first document
+ */
+
+ /* pos = zero + collection_name + NULL */
+ pos = sizeof (gint32) + strlen ("test.ns") + 1;
+ ok ((ins = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "First document is included");
+ bson_finish (ins);
+
+ ok ((c = bson_find (ins, "int32")) != NULL,
+ "BSON contains 'int32'");
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT32,
+ "int32 has correct type");
+ bson_cursor_next (c);
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT64,
+ "next element has correct type too");
+ ok (bson_cursor_next (c) == FALSE,
+ "No more data after the update BSON object");
+ bson_cursor_free (c);
+
+ /*
+ * Test the second document
+ */
+ pos += bson_size (ins);
+ ok ((tmp = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "Second document is included");
+ bson_finish (tmp);
+ cmp_ok (bson_size (tmp), "==", 5,
+ "Second document is empty");
+
+ bson_free (ins);
+ bson_free (tmp);
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (15, mongo_wire_cmd_insert);
diff --git a/tests/unit/mongo/wire/cmd_insert_n.c b/tests/unit/mongo/wire/cmd_insert_n.c
new file mode 100644
index 0000000..1c00193
--- /dev/null
+++ b/tests/unit/mongo/wire/cmd_insert_n.c
@@ -0,0 +1,95 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_cmd_insert_n (void)
+{
+ bson *ins, *tmp;
+ const bson *docs[10];
+ mongo_packet *p;
+
+ mongo_packet_header hdr;
+ const guint8 *data;
+ gint32 data_size;
+
+ bson_cursor *c;
+ gint32 pos;
+
+ ins = test_bson_generate_full ();
+ tmp = bson_new ();
+
+ docs[0] = ins;
+ docs[1] = tmp;
+ docs[2] = ins;
+ docs[3] = ins;
+ docs[4] = NULL;
+ docs[5] = ins;
+
+ ok (mongo_wire_cmd_insert_n (1, NULL, 1, docs) == NULL,
+ "mongo_wire_cmd_insert_n() fails with a NULL namespace");
+ ok (mongo_wire_cmd_insert_n (1, "test.ns", 1, NULL) == NULL,
+ "mongo_wire_cmd_insert_n() fails with no documents");
+ ok (mongo_wire_cmd_insert_n (1, "test.ns", 0, docs) == NULL,
+ "mongo_wire_cmd_insert_n() fails with no documents");
+ ok (mongo_wire_cmd_insert_n (1, "test.ns", 2, docs) == NULL,
+ "mongo_wire_cmd_insert_n() fails with an unfinished document");
+ bson_finish (tmp);
+ ok (mongo_wire_cmd_insert_n (1, "test.ns", 5, docs) == NULL,
+ "mongo_wire_cmd_insert_n() fails with a NULL document in the array");
+ ok ((p = mongo_wire_cmd_insert_n (1, "test.ns", 3, docs)) != NULL,
+ "mongo_wire_cmd_insert() works");
+ bson_free (ins);
+ bson_free (tmp);
+
+ /* Test basic header data */
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size appears fine");
+
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is correct");
+ cmp_ok (hdr.id, "==", 1, "Header ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Response ID is ok");
+
+ /*
+ * Test the first document
+ */
+
+ /* pos = zero + collection_name + NULL */
+ pos = sizeof (gint32) + strlen ("test.ns") + 1;
+ ok ((ins = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "First document is included");
+ bson_finish (ins);
+
+ ok ((c = bson_find (ins, "int32")) != NULL,
+ "BSON contains 'int32'");
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT32,
+ "int32 has correct type");
+ bson_cursor_next (c);
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT64,
+ "next element has correct type too");
+ ok (bson_cursor_next (c) == FALSE,
+ "No more data after the update BSON object");
+ bson_cursor_free (c);
+
+ /*
+ * Test the second document
+ */
+ pos += bson_size (ins);
+ ok ((tmp = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "Second document is included");
+ bson_finish (tmp);
+ cmp_ok (bson_size (tmp), "==", 5,
+ "Second document is empty");
+
+ bson_free (ins);
+ bson_free (tmp);
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (17, mongo_wire_cmd_insert_n);
diff --git a/tests/unit/mongo/wire/cmd_kill_cursors.c b/tests/unit/mongo/wire/cmd_kill_cursors.c
new file mode 100644
index 0000000..a8a8fd9
--- /dev/null
+++ b/tests/unit/mongo/wire/cmd_kill_cursors.c
@@ -0,0 +1,58 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_cmd_kill_cursors (void)
+{
+ mongo_packet *p;
+
+ mongo_packet_header hdr;
+ const guint8 *data;
+ gint32 data_size;
+
+ gint32 pos, n = 0;
+ gint64 c1 = 9876543210, c2 = 1234567890;
+
+ ok (mongo_wire_cmd_kill_cursors (1, 0) == NULL,
+ "mongo_wire_cmd_kill_cursors() should fail with zero cursors");
+ ok (mongo_wire_cmd_kill_cursors (1, -1) == NULL,
+ "mongo_wire_cmd_kill_cursors() should fail with negative amount of "
+ "cursors");
+ ok ((p = mongo_wire_cmd_kill_cursors (1, 2, c1, c2)) != NULL,
+ "mongo_wire_cmd_kill_cursors() works");
+
+ /* Verify the header */
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size looks fine");
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is OK");
+ cmp_ok (hdr.id, "==", 1, "Packet request ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Packet reply ID is ok");
+
+ /*
+ * Test the request contents
+ */
+ c1 = c2 = 0;
+ /* pos = zero + n */
+ pos = sizeof (gint32) + sizeof (n);
+
+ memcpy (&n, data + sizeof (gint32), sizeof (gint32));
+ memcpy (&c1, data + pos, sizeof (c1));
+ memcpy (&c2, data + pos + sizeof (c1), sizeof (c2));
+
+ n = GINT32_FROM_LE (n);
+ c1 = GINT64_FROM_LE (c1);
+ c2 = GINT64_FROM_LE (c2);
+
+ ok (n == 2, "Number of cursors are OK");
+ ok (c1 == 9876543210, "First cursor is OK");
+ ok (c2 == 1234567890, "Second cursor is OK");
+
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (10, mongo_wire_cmd_kill_cursors);
diff --git a/tests/unit/mongo/wire/cmd_query.c b/tests/unit/mongo/wire/cmd_query.c
new file mode 100644
index 0000000..58eb960
--- /dev/null
+++ b/tests/unit/mongo/wire/cmd_query.c
@@ -0,0 +1,117 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_cmd_query (void)
+{
+ bson *q, *s, *tmp;
+ mongo_packet *p;
+
+ mongo_packet_header hdr;
+ const guint8 *data;
+ gint32 data_size;
+
+ bson_cursor *c;
+ gint32 pos;
+
+ q = test_bson_generate_full ();
+ s = bson_new ();
+ bson_append_boolean (s, "_id", TRUE);
+ bson_append_boolean (s, "double", TRUE);
+ bson_finish (s);
+
+ tmp = bson_new ();
+
+ ok (mongo_wire_cmd_query (1, NULL, 0, 0, 0, q, s) == NULL,
+ "mongo_wire_cmd_query() fails whith a NULL namespace");
+ ok (mongo_wire_cmd_query (1, "test.ns", 0, 0, 0, NULL, s) == NULL,
+ "mongo_wire_cmd_query() fails with a NULL query");
+ ok (mongo_wire_cmd_query (1, "test.ns", 0, 0, 0, tmp, s) == NULL,
+ "mongo_wire_cmd_query() fails with an unfinished query");
+ ok (mongo_wire_cmd_query (1, "test.ns", 0, 0, 0, q, tmp) == NULL,
+ "mongo_wire_cmd_query() fails with an unfinished selector");
+ bson_free (tmp);
+
+ ok ((p = mongo_wire_cmd_query (1, "test.ns", 0, 0, 10, q, NULL)) != NULL,
+ "mongo_wire_cmd_query() works with a NULL selector");
+
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size looks fine");
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is OK");
+ cmp_ok (hdr.id, "==", 1, "Packet request ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Packet reply ID is ok");
+
+ /* pos = zero + collection_name + NULL + skip + ret */
+ pos = sizeof (gint32) + strlen ("test.ns") + 1 + sizeof (gint32) * 2;
+ ok ((tmp = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "Packet contains a valid BSON query document");
+ bson_finish (tmp);
+
+ ok ((c = bson_find (tmp, "int32")) != NULL,
+ "BSON contains 'int32'");
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT32,
+ "int32 has correct type");
+ bson_cursor_next (c);
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT64,
+ "next element has correct type too");
+ ok (bson_cursor_next (c) == FALSE,
+ "No more data after the update BSON object");
+ bson_cursor_free (c);
+
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + pos +
+ bson_size (q),
+ "Packet header lenght is correct");
+ bson_free (tmp);
+ mongo_wire_packet_free (p);
+
+ /*
+ * Test again with a selector document
+ */
+
+ ok ((p = mongo_wire_cmd_query (1, "test.ns", 0, 0, 10, q, s)) != NULL,
+ "mongo_wire_cmd_query() works with a NULL selector");
+
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size looks fine");
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is OK");
+ cmp_ok (hdr.id, "==", 1, "Packet request ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Packet reply ID is ok");
+
+ /* pos = zero + collection_name + NULL + skip + ret */
+ pos = sizeof (gint32) + strlen ("test.ns") + 1 + sizeof (gint32) * 2;
+ ok ((tmp = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "Packet contains a valid BSON query document");
+ bson_finish (tmp);
+ pos += bson_size (tmp);
+ bson_free (tmp);
+ bson_free (q);
+ bson_free (s);
+
+ ok ((s = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "Packet contains a valid BSON selector document");
+ bson_finish (s);
+
+ ok ((c = bson_find (s, "_id")) != NULL,
+ "BSON contains '_id'");
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_BOOLEAN,
+ "_id has correct type");
+ bson_cursor_next (c);
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_BOOLEAN,
+ "next element has correct type too");
+
+ bson_cursor_free (c);
+ bson_free (s);
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (25, mongo_wire_cmd_query);
diff --git a/tests/unit/mongo/wire/cmd_update.c b/tests/unit/mongo/wire/cmd_update.c
new file mode 100644
index 0000000..95a2a50
--- /dev/null
+++ b/tests/unit/mongo/wire/cmd_update.c
@@ -0,0 +1,97 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_cmd_update (void)
+{
+ bson *sel, *upd, *tmp;
+ mongo_packet *p;
+
+ mongo_packet_header hdr;
+ const guint8 *data;
+ gint32 data_size;
+
+ bson_cursor *c;
+ gint32 pos;
+
+ sel = bson_new ();
+ bson_append_null (sel, "_id");
+ bson_finish (sel);
+
+ upd = test_bson_generate_full ();
+
+ ok (mongo_wire_cmd_update (1, NULL, 0, sel, upd) == NULL,
+ "mongo_wire_cmd_update() with a NULL namespace should fail");
+ ok (mongo_wire_cmd_update (1, "test.ns", 0, NULL, upd) == NULL,
+ "mongo_wire_cmd_update() with a NULL selector should fail");
+ ok (mongo_wire_cmd_update (1, "test.ns", 0, sel, NULL) == NULL,
+ "mongo_wire_cmd_update() with a NULL update should fail");
+
+ tmp = bson_new ();
+ ok (mongo_wire_cmd_update (1, "test.ns", 0, tmp, upd) == NULL,
+ "mongo_wire_cmd_update() fails with an unfinished selector");
+ ok (mongo_wire_cmd_update (1, "test.ns", 0, sel, tmp) == NULL,
+ "mongo_wire_cmd_update() fails with an unfinished update");
+ bson_free (tmp);
+
+ ok ((p = mongo_wire_cmd_update (1, "test.ns", 0, sel, upd)) != NULL,
+ "mongo_wire_cmd_update() works");
+
+ bson_free (sel);
+
+ mongo_wire_packet_get_header (p, &hdr);
+ cmp_ok ((data_size = mongo_wire_packet_get_data (p, &data)), "!=", -1,
+ "Packet data size looks fine");
+ cmp_ok (hdr.length, "==", sizeof (mongo_packet_header) + data_size,
+ "Packet header length is OK");
+ cmp_ok (hdr.id, "==", 1, "Packet request ID is ok");
+ cmp_ok (hdr.resp_to, "==", 0, "Packet reply ID is ok");
+
+ /*
+ * Verify the selector object.
+ */
+
+ /* pos = zero + collection_name + NULL + flags */
+ pos = sizeof (gint32) + strlen ("test.ns") + 1 + sizeof (gint32);
+ ok ((sel = bson_new_from_data (data + pos, (gint32)data[pos] - 1)) != NULL,
+ "Packet contains a valid BSON selector document");
+ bson_finish (sel);
+
+ ok ((c = bson_find (sel, "_id")) != NULL,
+ "BSON contains an _id");
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_NULL,
+ "_id has correct type");
+ bson_cursor_free (c);
+ bson_free (sel);
+
+ /*
+ * Verify the update object
+ */
+ pos += (gint32)data[pos];
+ ok ((tmp = bson_new_from_data (data + pos,
+ bson_stream_doc_size (data, pos) - 1)) != NULL,
+ "Packet contains a valid BSON update document");
+ bson_finish (tmp);
+ cmp_ok (bson_size (upd), "==", bson_size (tmp),
+ "Packet's update document has the correct size");
+
+ ok ((c = bson_find (tmp, "int32")) != NULL,
+ "BSON contains 'int32'");
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT32,
+ "int32 has correct type");
+ bson_cursor_next (c);
+ cmp_ok (bson_cursor_type (c), "==", BSON_TYPE_INT64,
+ "next element has correct type too");
+ ok (bson_cursor_next (c) == FALSE,
+ "No more data after the update BSON object");
+
+ bson_cursor_free (c);
+ bson_free (tmp);
+ bson_free (upd);
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (19, mongo_wire_cmd_update);
diff --git a/tests/unit/mongo/wire/packet_get_set_data.c b/tests/unit/mongo/wire/packet_get_set_data.c
new file mode 100644
index 0000000..2f06b8f
--- /dev/null
+++ b/tests/unit/mongo/wire/packet_get_set_data.c
@@ -0,0 +1,65 @@
+#include "tap.h"
+#include "test.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_packet_get_set_data (void)
+{
+ mongo_packet *p;
+ mongo_packet_header h;
+ guint8 data[32768];
+ const guint8 *idata;
+
+ p = mongo_wire_packet_new ();
+ memset (data, 'x', sizeof (data));
+
+ ok (mongo_wire_packet_get_data (NULL, &idata) == -1,
+ "mongo_wire_packet_get_data() with a NULL packet should fail");
+ ok (mongo_wire_packet_get_data (p, NULL) == -1,
+ "mongo_wire_packet_get_data() with NULL destination should fail");
+ ok (mongo_wire_packet_get_data (p, &idata) == -1,
+ "mongo_wire_packet_get_data() with an empty packet should fail");
+ ok (mongo_wire_packet_set_data (NULL, (const guint8 *)&data,
+ sizeof (data)) == FALSE,
+ "mongo_wire_packet_set_data() with a NULL packet should fail");
+ ok (mongo_wire_packet_set_data (p, NULL, sizeof (data)) == FALSE,
+ "mongo_wire_packet_set_data() with NULL data should fail");
+ ok (mongo_wire_packet_set_data (p, (const guint8 *)&data, 0) == FALSE,
+ "mongo_wire_packet_set_data() with zero size should fail");
+ ok (mongo_wire_packet_set_data (p, (const guint8 *)&data, -1) == FALSE,
+ "mongo_wire_packet_set_data() with negative size should fail");
+
+ ok (mongo_wire_packet_set_data (p, (const guint8 *)&data,
+ sizeof (data)),
+ "mongo_wire_packet_set_data() works");
+ cmp_ok (mongo_wire_packet_get_data (p, &idata), "==", sizeof (data),
+ "mongo_wire_packet_get_data() works");
+
+ mongo_wire_packet_get_header (p, &h);
+
+ cmp_ok (h.length, "==", sizeof (data) + sizeof (mongo_packet_header),
+ "Packet length is updated properly");
+ ok (memcmp (data, idata, sizeof (data)) == 0,
+ "Setting & retrieving data works");
+
+ memset (data, 'a', sizeof (data));
+
+ ok (mongo_wire_packet_set_data (p, (const guint8 *)&data,
+ sizeof (data) / 2),
+ "Re-setting the data works");
+ cmp_ok (mongo_wire_packet_get_data (p, &idata), "==", sizeof (data) / 2,
+ "Retrieving the data works still");
+
+ mongo_wire_packet_get_header (p, &h);
+
+ cmp_ok (h.length, "==", sizeof (data) / 2 + sizeof (mongo_packet_header),
+ "Packet length is updated properly");
+ ok (memcmp (data, idata, sizeof (data) / 2) == 0,
+ "Setting & retrieving data works");
+
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (15, mongo_wire_packet_get_set_data);
diff --git a/tests/unit/mongo/wire/packet_get_set_header.c b/tests/unit/mongo/wire/packet_get_set_header.c
new file mode 100644
index 0000000..38e0ea7
--- /dev/null
+++ b/tests/unit/mongo/wire/packet_get_set_header.c
@@ -0,0 +1,58 @@
+#include "tap.h"
+#include "test.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_packet_get_set_header (void)
+{
+ mongo_packet *p;
+ mongo_packet_header ph1, ph2;
+
+ p = mongo_wire_packet_new ();
+
+ ok (mongo_wire_packet_get_header (NULL, &ph2) == FALSE,
+ "mongo_wire_packet_get_header() should fail with a NULL packet");
+ ok (mongo_wire_packet_get_header (p, NULL) == FALSE,
+ "mongo_wire_packet_get_header() should fail with a NULL header");
+ ok (mongo_wire_packet_set_header (NULL, &ph1) == FALSE,
+ "mongo_wire_packet_set_header() should fail with a NULL packet");
+ ok (mongo_wire_packet_set_header (p, NULL) == FALSE,
+ "mongo_wire_packet_set_header() should fail with a NULL header");
+
+ ok (mongo_wire_packet_get_header (p, &ph2),
+ "mongo_wire_packet_get_header() works on a fresh packet");
+ cmp_ok (ph2.length, "==", sizeof (mongo_packet_header),
+ "Initial packet length is the length of the header");
+
+ ph1.length = sizeof (mongo_packet_header);
+ ph1.id = 1;
+ ph1.resp_to = 0;
+ ph1.opcode = 1000;
+
+ memset (&ph2, 0, sizeof (mongo_packet_header));
+
+ ok (mongo_wire_packet_set_header (p, &ph1),
+ "mongo_wire_packet_set_header() works");
+ ok (mongo_wire_packet_get_header (p, &ph2),
+ "mongo_wire_packet_get_header() works");
+
+ cmp_ok (ph1.length, "==", ph2.length,
+ "Packet lengths match");
+ cmp_ok (ph1.id, "==", ph2.id,
+ "Sequence IDs match");
+ cmp_ok (ph1.resp_to, "==", ph2.resp_to,
+ "Response IDs match");
+ cmp_ok (ph1.opcode, "==", ph2.opcode,
+ "OPCodes match");
+
+ ph1.length = GINT32_TO_LE (1);
+ ok (mongo_wire_packet_set_header (p, &ph1) == FALSE,
+ "Setting a packet with length shorter than the header "
+ "returns an error");
+
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (13, mongo_wire_packet_get_set_header);
diff --git a/tests/unit/mongo/wire/packet_get_set_header_raw.c b/tests/unit/mongo/wire/packet_get_set_header_raw.c
new file mode 100644
index 0000000..d97a8b3
--- /dev/null
+++ b/tests/unit/mongo/wire/packet_get_set_header_raw.c
@@ -0,0 +1,56 @@
+#include "tap.h"
+#include "test.h"
+#include "mongo-wire.h"
+
+#include "libmongo-private.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_packet_get_set_header_raw (void)
+{
+ mongo_packet *p;
+ mongo_packet_header ph1, ph2;
+
+ p = mongo_wire_packet_new ();
+
+ ok (mongo_wire_packet_get_header_raw (NULL, &ph2) == FALSE,
+ "mongo_wire_packet_get_header_raw() should fail with a NULL packet");
+ ok (mongo_wire_packet_get_header_raw (p, NULL) == FALSE,
+ "mongo_wire_packet_get_header_raw() should fail with a NULL header");
+ ok (mongo_wire_packet_set_header_raw (NULL, &ph1) == FALSE,
+ "mongo_wire_packet_set_header_raw() should fail with a NULL packet");
+ ok (mongo_wire_packet_set_header_raw (p, NULL) == FALSE,
+ "mongo_wire_packet_set_header_raw() should fail with a NULL header");
+
+ ok (mongo_wire_packet_get_header_raw (p, &ph2),
+ "mongo_wire_packet_get_header_raw() works on a fresh packet");
+ /* Need to convert from LE, because _new() sets the length to LE. */
+ cmp_ok (GINT32_FROM_LE (ph2.length), "==", sizeof (mongo_packet_header),
+ "Initial packet length is the length of the header");
+
+ ph1.length = sizeof (mongo_packet_header);
+ ph1.id = 1;
+ ph1.resp_to = 0;
+ ph1.opcode = 1000;
+
+ memset (&ph2, 0, sizeof (mongo_packet_header));
+
+ ok (mongo_wire_packet_set_header_raw (p, &ph1),
+ "mongo_wire_packet_set_header_raw() works");
+ ok (mongo_wire_packet_get_header_raw (p, &ph2),
+ "mongo_wire_packet_get_header_raw() works");
+
+ cmp_ok (ph1.length, "==", ph2.length,
+ "Packet lengths match");
+ cmp_ok (ph1.id, "==", ph2.id,
+ "Sequence IDs match");
+ cmp_ok (ph1.resp_to, "==", ph2.resp_to,
+ "Response IDs match");
+ cmp_ok (ph1.opcode, "==", ph2.opcode,
+ "OPCodes match");
+
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (12, mongo_wire_packet_get_set_header_raw);
diff --git a/tests/unit/mongo/wire/packet_new.c b/tests/unit/mongo/wire/packet_new.c
new file mode 100644
index 0000000..4940542
--- /dev/null
+++ b/tests/unit/mongo/wire/packet_new.c
@@ -0,0 +1,20 @@
+#include "tap.h"
+#include "test.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_packet_new (void)
+{
+ mongo_packet *p;
+
+ ok ((p = mongo_wire_packet_new ()) != NULL,
+ "mongo_wire_packet_new() works");
+ mongo_wire_packet_free (NULL);
+ pass ("mongo_wire_packet_free(NULL) works");
+ mongo_wire_packet_free (p);
+ pass ("mongo_wire_packet_free() works");
+}
+
+RUN_TEST (3, mongo_wire_packet_new);
diff --git a/tests/unit/mongo/wire/reply_packet_get_data.c b/tests/unit/mongo/wire/reply_packet_get_data.c
new file mode 100644
index 0000000..e22f142
--- /dev/null
+++ b/tests/unit/mongo/wire/reply_packet_get_data.c
@@ -0,0 +1,52 @@
+#include "test.h"
+#include "tap.h"
+#include "bson.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_reply_packet_get_data (void)
+{
+ mongo_packet *p;
+ mongo_packet_header h;
+ const guint8 *data;
+ bson *b;
+
+ p = mongo_wire_packet_new ();
+ memset (&h, 0, sizeof (mongo_packet_header));
+ h.opcode = 0;
+ h.length = sizeof (mongo_packet_header);
+ mongo_wire_packet_set_header (p, &h);
+
+ ok (mongo_wire_reply_packet_get_data (NULL, &data) == FALSE,
+ "mongo_wire_reply_packet_get_data() fails with a NULL packet");
+ ok (mongo_wire_reply_packet_get_data (p, NULL) == FALSE,
+ "mongo_wire_reply_packet_get_data() fails with a NULL destination");
+ ok (mongo_wire_reply_packet_get_data (p, &data) == FALSE,
+ "mongo_wire_reply_packet_get_data() fails with a non-reply packet");
+
+ h.opcode = 1;
+ mongo_wire_packet_set_header (p, &h);
+
+ ok (mongo_wire_reply_packet_get_data (p, &data) == FALSE,
+ "mongo_wire_reply_packet_get_data() fails if the packet has "
+ "no data");
+
+ mongo_wire_packet_free (p);
+
+ p = test_mongo_wire_generate_reply (TRUE, 2, TRUE);
+
+ ok (mongo_wire_reply_packet_get_data (p, &data),
+ "mongo_wire_reply_packet_get_data() works");
+
+ b = test_bson_generate_full ();
+
+ ok (memcmp (data, bson_data (b), bson_size (b)) == 0,
+ "The returned data is correct");
+
+ bson_free (b);
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (6, mongo_wire_reply_packet_get_data);
diff --git a/tests/unit/mongo/wire/reply_packet_get_header.c b/tests/unit/mongo/wire/reply_packet_get_header.c
new file mode 100644
index 0000000..36b548c
--- /dev/null
+++ b/tests/unit/mongo/wire/reply_packet_get_header.c
@@ -0,0 +1,54 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_reply_packet_get_header (void)
+{
+ mongo_packet *p;
+ mongo_packet_header h;
+ mongo_reply_packet_header rh;
+
+ p = mongo_wire_packet_new ();
+ memset (&h, 0, sizeof (mongo_packet_header));
+ h.opcode = 1;
+ h.length = sizeof (mongo_packet_header);
+
+ mongo_wire_packet_set_header (p, &h);
+
+ ok (mongo_wire_reply_packet_get_header (NULL, &rh) == FALSE,
+ "mongo_wire_reply_packet_get_header() fails with a NULL packet");
+ ok (mongo_wire_reply_packet_get_header (p, NULL) == FALSE,
+ "mongo_wire_reply_packet_get_header() fails with a NULL header");
+
+ ok (mongo_wire_reply_packet_get_header (p, &rh) == FALSE,
+ "mongo_wire_reply_packet_get_header() fails if the packet has "
+ "no reply header");
+
+ h.opcode = 2;
+ mongo_wire_packet_set_header (p, &h);
+ ok (mongo_wire_reply_packet_get_header (p, &rh) == FALSE,
+ "mongo_wire_reply_packet_get_header() fails if the packet is "
+ "not a reply packet");
+
+ mongo_wire_packet_free (p);
+
+ p = test_mongo_wire_generate_reply (TRUE, 0, FALSE);
+
+ ok (mongo_wire_reply_packet_get_header (p, &rh),
+ "mongo_wire_reply_packet_get_header() works");
+ cmp_ok (rh.flags, "==", 0,
+ "Reply flags are correct");
+ ok (rh.cursor_id == (gint64)12345,
+ "Cursor ID is correct");
+ cmp_ok (rh.start, "==", 0,
+ "Reply start document is OK");
+ cmp_ok (rh.returned, "==", 0,
+ "Number of documents returned is OK");
+
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (9, mongo_wire_reply_packet_get_header);
diff --git a/tests/unit/mongo/wire/reply_packet_get_nth_document.c b/tests/unit/mongo/wire/reply_packet_get_nth_document.c
new file mode 100644
index 0000000..68d9fed
--- /dev/null
+++ b/tests/unit/mongo/wire/reply_packet_get_nth_document.c
@@ -0,0 +1,68 @@
+#include "test.h"
+#include "tap.h"
+#include "mongo-wire.h"
+#include "bson.h"
+
+#include <string.h>
+
+void
+test_mongo_wire_reply_packet_get_nth_document (void)
+{
+ mongo_packet *p;
+ bson *b, *doc;
+ mongo_packet_header h;
+
+ p = mongo_wire_packet_new ();
+ memset (&h, 0, sizeof (mongo_packet_header));
+ h.opcode = 2;
+ h.length = sizeof (mongo_packet_header);
+ mongo_wire_packet_set_header (p, &h);
+
+ ok (mongo_wire_reply_packet_get_nth_document (NULL, 1, &doc) == FALSE,
+ "mongo_wire_reply_packet_get_nth_document() fails with a NULL packet");
+ ok (mongo_wire_reply_packet_get_nth_document (p, 0, &doc) == FALSE,
+ "mongo_wire_reply_packet_get_nth_document() fails with n = 0");
+ ok (mongo_wire_reply_packet_get_nth_document (p, -42, &doc) == FALSE,
+ "mongo_wire_reply_packet_get_nth_document() fails with n < 0");
+ ok (mongo_wire_reply_packet_get_nth_document (p, 1, NULL) == FALSE,
+ "mongo_wire_reply_packet_get_nth_document() fails with a NULL "
+ "destination");
+
+ ok (mongo_wire_reply_packet_get_nth_document (p, 1, &doc) == FALSE,
+ "mongo_wire_reply_packet_get_nth_document() fails with a "
+ "non-reply packet");
+
+ h.opcode = 1;
+ mongo_wire_packet_set_header (p, &h);
+
+ ok (mongo_wire_reply_packet_get_nth_document (p, 1, &doc) == FALSE,
+ "mongo_wire_reply_packet_get_nth_document() fails with an "
+ "incomplete reply packet");
+
+ mongo_wire_packet_free (p);
+
+ p = test_mongo_wire_generate_reply (TRUE, 0, FALSE);
+ ok (mongo_wire_reply_packet_get_nth_document (p, 1, &doc) == FALSE,
+ "mongo_wire_reply_packet_get_nth_document() fails if there are "
+ "no documents to return");
+ mongo_wire_packet_free (p);
+
+ p = test_mongo_wire_generate_reply (TRUE, 2, TRUE);
+ ok (mongo_wire_reply_packet_get_nth_document (p, 2, &doc),
+ "mongo_wire_reply_packet_get_nth_document() works");
+ b = test_bson_generate_full ();
+ bson_finish (doc);
+
+ ok (memcmp (bson_data (b), bson_data (doc), bson_size (doc)) == 0,
+ "Returned document is correct");
+ bson_free (doc);
+ bson_free (b);
+
+ ok (mongo_wire_reply_packet_get_nth_document (p, 3, &doc) == FALSE,
+ "mongo_wire_reply_packet_get_nth_document() fails if the requested "
+ "document does not exist");
+
+ mongo_wire_packet_free (p);
+}
+
+RUN_TEST (10, mongo_wire_reply_packet_get_nth_document);