|  | /* | 
|  | * Block replication tests | 
|  | * | 
|  | * Copyright (c) 2016 FUJITSU LIMITED | 
|  | * Author: Changlong Xie <xiecl.fnst@cn.fujitsu.com> | 
|  | * | 
|  | * This work is licensed under the terms of the GNU GPL, version 2 or | 
|  | * later.  See the COPYING file in the top-level directory. | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  |  | 
|  | #include "qapi/error.h" | 
|  | #include "qobject/qdict.h" | 
|  | #include "qemu/option.h" | 
|  | #include "qemu/main-loop.h" | 
|  | #include "block/replication.h" | 
|  | #include "block/block_int.h" | 
|  | #include "block/qdict.h" | 
|  | #include "system/block-backend.h" | 
|  |  | 
|  | #define IMG_SIZE (64 * 1024 * 1024) | 
|  |  | 
|  | /* primary */ | 
|  | #define P_ID "primary-id" | 
|  | static char *p_local_disk; | 
|  |  | 
|  | /* secondary */ | 
|  | #define S_ID "secondary-id" | 
|  | #define S_LOCAL_DISK_ID "secondary-local-disk-id" | 
|  | static char *s_local_disk; | 
|  | static char *s_active_disk; | 
|  | static char *s_hidden_disk; | 
|  |  | 
|  | /* FIXME: steal from blockdev.c */ | 
|  | QemuOptsList qemu_drive_opts = { | 
|  | .name = "drive", | 
|  | .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), | 
|  | .desc = { | 
|  | { /* end of list */ } | 
|  | }, | 
|  | }; | 
|  |  | 
|  | #define NOT_DONE 0x7fffffff | 
|  |  | 
|  | static void blk_rw_done(void *opaque, int ret) | 
|  | { | 
|  | *(int *)opaque = ret; | 
|  | } | 
|  |  | 
|  | static void test_blk_read(BlockBackend *blk, long pattern, | 
|  | int64_t pattern_offset, int64_t pattern_count, | 
|  | int64_t offset, int64_t count, | 
|  | bool expect_failed) | 
|  | { | 
|  | void *pattern_buf = NULL; | 
|  | QEMUIOVector qiov; | 
|  | void *cmp_buf = NULL; | 
|  | int async_ret = NOT_DONE; | 
|  |  | 
|  | if (pattern) { | 
|  | cmp_buf = g_malloc(pattern_count); | 
|  | memset(cmp_buf, pattern, pattern_count); | 
|  | } | 
|  |  | 
|  | pattern_buf = g_malloc(count); | 
|  | if (pattern) { | 
|  | memset(pattern_buf, pattern, count); | 
|  | } else { | 
|  | memset(pattern_buf, 0x00, count); | 
|  | } | 
|  |  | 
|  | qemu_iovec_init(&qiov, 1); | 
|  | qemu_iovec_add(&qiov, pattern_buf, count); | 
|  |  | 
|  | blk_aio_preadv(blk, offset, &qiov, 0, blk_rw_done, &async_ret); | 
|  | while (async_ret == NOT_DONE) { | 
|  | main_loop_wait(false); | 
|  | } | 
|  |  | 
|  | if (expect_failed) { | 
|  | g_assert(async_ret != 0); | 
|  | } else { | 
|  | g_assert(async_ret == 0); | 
|  | if (pattern) { | 
|  | g_assert(memcmp(pattern_buf + pattern_offset, | 
|  | cmp_buf, pattern_count) <= 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | g_free(pattern_buf); | 
|  | g_free(cmp_buf); | 
|  | qemu_iovec_destroy(&qiov); | 
|  | } | 
|  |  | 
|  | static void test_blk_write(BlockBackend *blk, long pattern, int64_t offset, | 
|  | int64_t count, bool expect_failed) | 
|  | { | 
|  | void *pattern_buf = NULL; | 
|  | QEMUIOVector qiov; | 
|  | int async_ret = NOT_DONE; | 
|  |  | 
|  | pattern_buf = g_malloc(count); | 
|  | if (pattern) { | 
|  | memset(pattern_buf, pattern, count); | 
|  | } else { | 
|  | memset(pattern_buf, 0x00, count); | 
|  | } | 
|  |  | 
|  | qemu_iovec_init(&qiov, 1); | 
|  | qemu_iovec_add(&qiov, pattern_buf, count); | 
|  |  | 
|  | blk_aio_pwritev(blk, offset, &qiov, 0, blk_rw_done, &async_ret); | 
|  | while (async_ret == NOT_DONE) { | 
|  | main_loop_wait(false); | 
|  | } | 
|  |  | 
|  | if (expect_failed) { | 
|  | g_assert(async_ret != 0); | 
|  | } else { | 
|  | g_assert(async_ret == 0); | 
|  | } | 
|  |  | 
|  | g_free(pattern_buf); | 
|  | qemu_iovec_destroy(&qiov); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Create a uniquely-named empty temporary file. | 
|  | */ | 
|  | static void make_temp(char *template) | 
|  | { | 
|  | int fd; | 
|  |  | 
|  | fd = mkstemp(template); | 
|  | g_assert(fd >= 0); | 
|  | close(fd); | 
|  | } | 
|  |  | 
|  | static void prepare_imgs(void) | 
|  | { | 
|  | make_temp(p_local_disk); | 
|  | make_temp(s_local_disk); | 
|  | make_temp(s_active_disk); | 
|  | make_temp(s_hidden_disk); | 
|  |  | 
|  | /* Primary */ | 
|  | bdrv_img_create(p_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, | 
|  | BDRV_O_RDWR, true, &error_abort); | 
|  |  | 
|  | /* Secondary */ | 
|  | bdrv_img_create(s_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, | 
|  | BDRV_O_RDWR, true, &error_abort); | 
|  | bdrv_img_create(s_active_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, | 
|  | BDRV_O_RDWR, true, &error_abort); | 
|  | bdrv_img_create(s_hidden_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, | 
|  | BDRV_O_RDWR, true, &error_abort); | 
|  | } | 
|  |  | 
|  | static void cleanup_imgs(void) | 
|  | { | 
|  | /* Primary */ | 
|  | unlink(p_local_disk); | 
|  |  | 
|  | /* Secondary */ | 
|  | unlink(s_local_disk); | 
|  | unlink(s_active_disk); | 
|  | unlink(s_hidden_disk); | 
|  | } | 
|  |  | 
|  | static BlockBackend *start_primary(void) | 
|  | { | 
|  | BlockBackend *blk; | 
|  | QemuOpts *opts; | 
|  | QDict *qdict; | 
|  | char *cmdline; | 
|  |  | 
|  | cmdline = g_strdup_printf("driver=replication,mode=primary,node-name=xxx," | 
|  | "file.driver=qcow2,file.file.filename=%s," | 
|  | "file.file.locking=off" | 
|  | , p_local_disk); | 
|  | opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); | 
|  | g_free(cmdline); | 
|  |  | 
|  | qdict = qemu_opts_to_qdict(opts, NULL); | 
|  | qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); | 
|  | qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); | 
|  |  | 
|  | blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); | 
|  | g_assert(blk); | 
|  |  | 
|  | monitor_add_blk(blk, P_ID, &error_abort); | 
|  |  | 
|  | qemu_opts_del(opts); | 
|  |  | 
|  | return blk; | 
|  | } | 
|  |  | 
|  | static void teardown_primary(void) | 
|  | { | 
|  | BlockBackend *blk; | 
|  |  | 
|  | /* remove P_ID */ | 
|  | blk = blk_by_name(P_ID); | 
|  | assert(blk); | 
|  |  | 
|  | monitor_remove_blk(blk); | 
|  | blk_unref(blk); | 
|  | } | 
|  |  | 
|  | static void test_primary_read(void) | 
|  | { | 
|  | BlockBackend *blk; | 
|  |  | 
|  | blk = start_primary(); | 
|  |  | 
|  | /* read from 0 to IMG_SIZE */ | 
|  | test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); | 
|  |  | 
|  | teardown_primary(); | 
|  | } | 
|  |  | 
|  | static void test_primary_write(void) | 
|  | { | 
|  | BlockBackend *blk; | 
|  |  | 
|  | blk = start_primary(); | 
|  |  | 
|  | /* write from 0 to IMG_SIZE */ | 
|  | test_blk_write(blk, 0, 0, IMG_SIZE, true); | 
|  |  | 
|  | teardown_primary(); | 
|  | } | 
|  |  | 
|  | static void test_primary_start(void) | 
|  | { | 
|  | BlockBackend *blk = NULL; | 
|  |  | 
|  | blk = start_primary(); | 
|  |  | 
|  | replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); | 
|  |  | 
|  | /* read from 0 to IMG_SIZE */ | 
|  | test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); | 
|  |  | 
|  | /* write 0x22 from 0 to IMG_SIZE */ | 
|  | test_blk_write(blk, 0x22, 0, IMG_SIZE, false); | 
|  |  | 
|  | teardown_primary(); | 
|  | } | 
|  |  | 
|  | static void test_primary_stop(void) | 
|  | { | 
|  | bool failover = true; | 
|  |  | 
|  | start_primary(); | 
|  |  | 
|  | replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); | 
|  |  | 
|  | replication_stop_all(failover, &error_abort); | 
|  |  | 
|  | teardown_primary(); | 
|  | } | 
|  |  | 
|  | static void test_primary_do_checkpoint(void) | 
|  | { | 
|  | start_primary(); | 
|  |  | 
|  | replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); | 
|  |  | 
|  | replication_do_checkpoint_all(&error_abort); | 
|  |  | 
|  | teardown_primary(); | 
|  | } | 
|  |  | 
|  | static void test_primary_get_error_all(void) | 
|  | { | 
|  | start_primary(); | 
|  |  | 
|  | replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); | 
|  |  | 
|  | replication_get_error_all(&error_abort); | 
|  |  | 
|  | teardown_primary(); | 
|  | } | 
|  |  | 
|  | static BlockBackend *start_secondary(void) | 
|  | { | 
|  | QemuOpts *opts; | 
|  | QDict *qdict; | 
|  | BlockBackend *blk; | 
|  | char *cmdline; | 
|  |  | 
|  | /* add s_local_disk and forge S_LOCAL_DISK_ID */ | 
|  | cmdline = g_strdup_printf("file.filename=%s,driver=qcow2," | 
|  | "file.locking=off", | 
|  | s_local_disk); | 
|  | opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); | 
|  | g_free(cmdline); | 
|  |  | 
|  | qdict = qemu_opts_to_qdict(opts, NULL); | 
|  | qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); | 
|  | qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); | 
|  |  | 
|  | blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); | 
|  | assert(blk); | 
|  | monitor_add_blk(blk, S_LOCAL_DISK_ID, &error_abort); | 
|  |  | 
|  | /* format s_local_disk with pattern "0x11" */ | 
|  | test_blk_write(blk, 0x11, 0, IMG_SIZE, false); | 
|  |  | 
|  | qemu_opts_del(opts); | 
|  |  | 
|  | /* add S_(ACTIVE/HIDDEN)_DISK and forge S_ID */ | 
|  | cmdline = g_strdup_printf("driver=replication,mode=secondary,top-id=%s," | 
|  | "file.driver=qcow2,file.file.filename=%s," | 
|  | "file.file.locking=off," | 
|  | "file.backing.driver=qcow2," | 
|  | "file.backing.file.filename=%s," | 
|  | "file.backing.file.locking=off," | 
|  | "file.backing.backing=%s" | 
|  | , S_ID, s_active_disk, s_hidden_disk | 
|  | , S_LOCAL_DISK_ID); | 
|  | opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); | 
|  | g_free(cmdline); | 
|  |  | 
|  | qdict = qemu_opts_to_qdict(opts, NULL); | 
|  | qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); | 
|  | qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); | 
|  |  | 
|  | blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); | 
|  | assert(blk); | 
|  | monitor_add_blk(blk, S_ID, &error_abort); | 
|  |  | 
|  | qemu_opts_del(opts); | 
|  |  | 
|  | return blk; | 
|  | } | 
|  |  | 
|  | static void teardown_secondary(void) | 
|  | { | 
|  | /* only need to destroy two BBs */ | 
|  | BlockBackend *blk; | 
|  |  | 
|  | /* remove S_LOCAL_DISK_ID */ | 
|  | blk = blk_by_name(S_LOCAL_DISK_ID); | 
|  | assert(blk); | 
|  |  | 
|  | monitor_remove_blk(blk); | 
|  | blk_unref(blk); | 
|  |  | 
|  | /* remove S_ID */ | 
|  | blk = blk_by_name(S_ID); | 
|  | assert(blk); | 
|  |  | 
|  | monitor_remove_blk(blk); | 
|  | blk_unref(blk); | 
|  | } | 
|  |  | 
|  | static void test_secondary_read(void) | 
|  | { | 
|  | BlockBackend *blk; | 
|  |  | 
|  | blk = start_secondary(); | 
|  |  | 
|  | /* read from 0 to IMG_SIZE */ | 
|  | test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); | 
|  |  | 
|  | teardown_secondary(); | 
|  | } | 
|  |  | 
|  | static void test_secondary_write(void) | 
|  | { | 
|  | BlockBackend *blk; | 
|  |  | 
|  | blk = start_secondary(); | 
|  |  | 
|  | /* write from 0 to IMG_SIZE */ | 
|  | test_blk_write(blk, 0, 0, IMG_SIZE, true); | 
|  |  | 
|  | teardown_secondary(); | 
|  | } | 
|  |  | 
|  | #ifndef _WIN32 | 
|  | static void test_secondary_start(void) | 
|  | { | 
|  | BlockBackend *top_blk, *local_blk; | 
|  | bool failover = true; | 
|  |  | 
|  | top_blk = start_secondary(); | 
|  | replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); | 
|  |  | 
|  | /* read from s_local_disk (0, IMG_SIZE) */ | 
|  | test_blk_read(top_blk, 0x11, 0, IMG_SIZE, 0, IMG_SIZE, false); | 
|  |  | 
|  | /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ | 
|  | local_blk = blk_by_name(S_LOCAL_DISK_ID); | 
|  | test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); | 
|  |  | 
|  | /* replication will backup s_local_disk to s_hidden_disk */ | 
|  | test_blk_read(top_blk, 0x11, IMG_SIZE / 2, | 
|  | IMG_SIZE / 2, 0, IMG_SIZE, false); | 
|  |  | 
|  | /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ | 
|  | test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); | 
|  |  | 
|  | /* read from s_active_disk (0, IMG_SIZE/2) */ | 
|  | test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, | 
|  | 0, IMG_SIZE / 2, false); | 
|  |  | 
|  | /* unblock top_bs */ | 
|  | replication_stop_all(failover, &error_abort); | 
|  |  | 
|  | teardown_secondary(); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void test_secondary_stop(void) | 
|  | { | 
|  | BlockBackend *top_blk, *local_blk; | 
|  | bool failover = true; | 
|  |  | 
|  | top_blk = start_secondary(); | 
|  | replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); | 
|  |  | 
|  | /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ | 
|  | local_blk = blk_by_name(S_LOCAL_DISK_ID); | 
|  | test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); | 
|  |  | 
|  | /* replication will backup s_local_disk to s_hidden_disk */ | 
|  | test_blk_read(top_blk, 0x11, IMG_SIZE / 2, | 
|  | IMG_SIZE / 2, 0, IMG_SIZE, false); | 
|  |  | 
|  | /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ | 
|  | test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); | 
|  |  | 
|  | /* do active commit */ | 
|  | replication_stop_all(failover, &error_abort); | 
|  |  | 
|  | /* read from s_local_disk (0, IMG_SIZE / 2) */ | 
|  | test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, | 
|  | 0, IMG_SIZE / 2, false); | 
|  |  | 
|  |  | 
|  | /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ | 
|  | test_blk_read(top_blk, 0x22, IMG_SIZE / 2, | 
|  | IMG_SIZE / 2, 0, IMG_SIZE, false); | 
|  |  | 
|  | teardown_secondary(); | 
|  | } | 
|  |  | 
|  | static void test_secondary_continuous_replication(void) | 
|  | { | 
|  | BlockBackend *top_blk, *local_blk; | 
|  |  | 
|  | top_blk = start_secondary(); | 
|  | replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); | 
|  |  | 
|  | /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ | 
|  | local_blk = blk_by_name(S_LOCAL_DISK_ID); | 
|  | test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); | 
|  |  | 
|  | /* replication will backup s_local_disk to s_hidden_disk */ | 
|  | test_blk_read(top_blk, 0x11, IMG_SIZE / 2, | 
|  | IMG_SIZE / 2, 0, IMG_SIZE, false); | 
|  |  | 
|  | /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ | 
|  | test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); | 
|  |  | 
|  | /* do failover (active commit) */ | 
|  | replication_stop_all(true, &error_abort); | 
|  |  | 
|  | /* it should ignore all requests from now on */ | 
|  |  | 
|  | /* start after failover */ | 
|  | replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); | 
|  |  | 
|  | /* checkpoint */ | 
|  | replication_do_checkpoint_all(&error_abort); | 
|  |  | 
|  | /* stop */ | 
|  | replication_stop_all(true, &error_abort); | 
|  |  | 
|  | /* read from s_local_disk (0, IMG_SIZE / 2) */ | 
|  | test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, | 
|  | 0, IMG_SIZE / 2, false); | 
|  |  | 
|  |  | 
|  | /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ | 
|  | test_blk_read(top_blk, 0x22, IMG_SIZE / 2, | 
|  | IMG_SIZE / 2, 0, IMG_SIZE, false); | 
|  |  | 
|  | teardown_secondary(); | 
|  | } | 
|  |  | 
|  | static void test_secondary_do_checkpoint(void) | 
|  | { | 
|  | BlockBackend *top_blk, *local_blk; | 
|  | bool failover = true; | 
|  |  | 
|  | top_blk = start_secondary(); | 
|  | replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); | 
|  |  | 
|  | /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ | 
|  | local_blk = blk_by_name(S_LOCAL_DISK_ID); | 
|  | test_blk_write(local_blk, 0x22, IMG_SIZE / 2, | 
|  | IMG_SIZE / 2, false); | 
|  |  | 
|  | /* replication will backup s_local_disk to s_hidden_disk */ | 
|  | test_blk_read(top_blk, 0x11, IMG_SIZE / 2, | 
|  | IMG_SIZE / 2, 0, IMG_SIZE, false); | 
|  |  | 
|  | replication_do_checkpoint_all(&error_abort); | 
|  |  | 
|  | /* after checkpoint, read pattern 0x22 from s_local_disk */ | 
|  | test_blk_read(top_blk, 0x22, IMG_SIZE / 2, | 
|  | IMG_SIZE / 2, 0, IMG_SIZE, false); | 
|  |  | 
|  | /* unblock top_bs */ | 
|  | replication_stop_all(failover, &error_abort); | 
|  |  | 
|  | teardown_secondary(); | 
|  | } | 
|  |  | 
|  | static void test_secondary_get_error_all(void) | 
|  | { | 
|  | bool failover = true; | 
|  |  | 
|  | start_secondary(); | 
|  | replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); | 
|  |  | 
|  | replication_get_error_all(&error_abort); | 
|  |  | 
|  | /* unblock top_bs */ | 
|  | replication_stop_all(failover, &error_abort); | 
|  |  | 
|  | teardown_secondary(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void sigabrt_handler(int signo) | 
|  | { | 
|  | cleanup_imgs(); | 
|  | } | 
|  |  | 
|  | static void setup_sigabrt_handler(void) | 
|  | { | 
|  | #ifdef _WIN32 | 
|  | signal(SIGABRT, sigabrt_handler); | 
|  | #else | 
|  | struct sigaction sigact; | 
|  |  | 
|  | sigact = (struct sigaction) { | 
|  | .sa_handler = sigabrt_handler, | 
|  | .sa_flags = SA_RESETHAND, | 
|  | }; | 
|  | sigemptyset(&sigact.sa_mask); | 
|  | sigaction(SIGABRT, &sigact, NULL); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | int main(int argc, char **argv) | 
|  | { | 
|  | int ret; | 
|  | const char *tmpdir = g_get_tmp_dir(); | 
|  | p_local_disk = g_strdup_printf("%s/p_local_disk.XXXXXX", tmpdir); | 
|  | s_local_disk = g_strdup_printf("%s/s_local_disk.XXXXXX", tmpdir); | 
|  | s_active_disk = g_strdup_printf("%s/s_active_disk.XXXXXX", tmpdir); | 
|  | s_hidden_disk = g_strdup_printf("%s/s_hidden_disk.XXXXXX", tmpdir); | 
|  | qemu_init_main_loop(&error_fatal); | 
|  | bdrv_init(); | 
|  |  | 
|  | g_test_init(&argc, &argv, NULL); | 
|  | setup_sigabrt_handler(); | 
|  |  | 
|  | prepare_imgs(); | 
|  |  | 
|  | /* Primary */ | 
|  | g_test_add_func("/replication/primary/read",    test_primary_read); | 
|  | g_test_add_func("/replication/primary/write",   test_primary_write); | 
|  | g_test_add_func("/replication/primary/start",   test_primary_start); | 
|  | g_test_add_func("/replication/primary/stop",    test_primary_stop); | 
|  | g_test_add_func("/replication/primary/do_checkpoint", | 
|  | test_primary_do_checkpoint); | 
|  | g_test_add_func("/replication/primary/get_error_all", | 
|  | test_primary_get_error_all); | 
|  |  | 
|  | /* Secondary */ | 
|  | g_test_add_func("/replication/secondary/read",  test_secondary_read); | 
|  | g_test_add_func("/replication/secondary/write", test_secondary_write); | 
|  | #ifndef _WIN32 | 
|  | g_test_add_func("/replication/secondary/start", test_secondary_start); | 
|  | g_test_add_func("/replication/secondary/stop",  test_secondary_stop); | 
|  | g_test_add_func("/replication/secondary/continuous_replication", | 
|  | test_secondary_continuous_replication); | 
|  | g_test_add_func("/replication/secondary/do_checkpoint", | 
|  | test_secondary_do_checkpoint); | 
|  | g_test_add_func("/replication/secondary/get_error_all", | 
|  | test_secondary_get_error_all); | 
|  | #endif | 
|  |  | 
|  | ret = g_test_run(); | 
|  |  | 
|  | cleanup_imgs(); | 
|  |  | 
|  | g_free(p_local_disk); | 
|  | g_free(s_local_disk); | 
|  | g_free(s_active_disk); | 
|  | g_free(s_hidden_disk); | 
|  |  | 
|  | return ret; | 
|  | } |