You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
17 KiB
560 lines
17 KiB
/*
|
|
* mock-sync - A mock-plugin for the opensync framework
|
|
* Copyright (C) 2004-2005 Armin Bauer <armin.bauer@opensync.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include "mock_sync.h"
|
|
|
|
#define fail(x) abort()
|
|
|
|
#define fail_unless(condition, msg) do { \
|
|
if (!condition) fail(msg); \
|
|
} while (0)
|
|
|
|
int mock_custom_function(mock_env *env, int input, OSyncError **error)
|
|
{
|
|
osync_trace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, env, input, error);
|
|
|
|
fail_unless(input == 1, NULL);
|
|
|
|
osync_trace(TRACE_EXIT, "%s", __func__);
|
|
return 2;
|
|
}
|
|
|
|
/*Load the state from a xml file and return it in the conn struct*/
|
|
static osync_bool mock_parse_settings(mock_env *env, char *data, int size, OSyncError **error)
|
|
{
|
|
osync_trace(TRACE_ENTRY, "%s(%p, %p, %i)", __func__, env, data, size);
|
|
xmlDocPtr doc;
|
|
xmlNodePtr cur;
|
|
|
|
//set defaults
|
|
env->path = NULL;
|
|
|
|
doc = xmlParseMemory(data, size);
|
|
|
|
if (!doc) {
|
|
osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to parse settings");
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
|
|
return FALSE;
|
|
}
|
|
|
|
cur = xmlDocGetRootElement(doc);
|
|
|
|
if (!cur) {
|
|
xmlFreeDoc(doc);
|
|
osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to get root element of the settings");
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
|
|
return FALSE;
|
|
}
|
|
|
|
if (xmlStrcmp(cur->name, (xmlChar*)"config")) {
|
|
xmlFreeDoc(doc);
|
|
osync_error_set(error, OSYNC_ERROR_GENERIC, "Config valid is not valid");
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
|
|
return FALSE;
|
|
}
|
|
|
|
cur = cur->xmlChildrenNode;
|
|
|
|
while (cur != NULL) {
|
|
char *str = (char*)xmlNodeGetContent(cur);
|
|
if (str) {
|
|
if (!xmlStrcmp(cur->name, (const xmlChar *)"path")) {
|
|
env->path = g_strdup(str);
|
|
}
|
|
xmlFree(str);
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
|
|
xmlFreeDoc(doc);
|
|
osync_trace(TRACE_EXIT, "%s", __func__);
|
|
return TRUE;
|
|
}
|
|
|
|
static osync_bool mock_get_error(OSyncMember *member, const char *domain)
|
|
{
|
|
const char *env = g_getenv(domain);
|
|
if (!env)
|
|
return FALSE;
|
|
|
|
int num = atoi(env);
|
|
int mask = 1 << (osync_member_get_id(member) - 1);
|
|
if (num & mask) {
|
|
char *chancestr = g_strdup_printf("%s_PROB", domain);
|
|
const char *chance = g_getenv(chancestr);
|
|
g_free(chancestr);
|
|
if (!chance)
|
|
return TRUE;
|
|
int prob = atoi(chance);
|
|
if (prob >= g_random_int_range(0, 100))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void *mock_initialize(OSyncMember *member, OSyncError **error)
|
|
{
|
|
osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, member, error);
|
|
|
|
if (mock_get_error(member, "INIT_NULL")) {
|
|
osync_error_set(error, OSYNC_ERROR_EXPECTED, "Triggering INIT_NULL error");
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
|
|
return NULL;
|
|
}
|
|
char *configdata;
|
|
int configsize;
|
|
mock_env *env = g_malloc0(sizeof(mock_env));
|
|
|
|
if (!osync_member_get_config(member, &configdata, &configsize, error)) {
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
|
|
return NULL;
|
|
}
|
|
|
|
if (!mock_parse_settings(env, configdata, configsize, error)) {
|
|
g_free(env);
|
|
return NULL;
|
|
}
|
|
|
|
//Rewrite the batch commit functions so we can disable them if necessary
|
|
if (!mock_get_error(member, "BATCH_COMMIT")) {
|
|
OSyncObjFormatSink *fmtsink = member->format_sinks->data;
|
|
osync_trace(TRACE_INTERNAL, "Disabling batch_commit on %p:%s: %i", fmtsink, fmtsink->format ? fmtsink->format->name : "None", g_list_length(member->format_sinks));
|
|
OSyncFormatFunctions *functions = &(fmtsink->functions);
|
|
functions->batch_commit = NULL;
|
|
}
|
|
|
|
env->member = member;
|
|
env->hashtable = osync_hashtable_new();
|
|
|
|
osync_trace(TRACE_EXIT, "%s: %p", __func__, env);
|
|
return (void *)env;
|
|
}
|
|
|
|
static void mock_connect(OSyncContext *ctx)
|
|
{
|
|
osync_trace(TRACE_ENTRY, "%s(%p)", __func__, ctx);
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(ctx);
|
|
|
|
env->committed_all = TRUE;
|
|
|
|
if (mock_get_error(env->member, "CONNECT_ERROR")) {
|
|
osync_context_report_error(ctx, OSYNC_ERROR_EXPECTED, "Triggering CONNECT_ERROR error");
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, "Triggering CONNECT_ERROR error");
|
|
return;
|
|
}
|
|
|
|
if (mock_get_error(env->member, "CONNECT_TIMEOUT")) {
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, "Triggering CONNECT_TIMEOUT error");
|
|
return;
|
|
}
|
|
|
|
OSyncError *error = NULL;
|
|
if (!osync_hashtable_load(env->hashtable, env->member, &error)) {
|
|
osync_context_report_osyncerror(ctx, &error);
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
|
|
osync_error_free(&error);
|
|
return;
|
|
}
|
|
|
|
if (!osync_anchor_compare(env->member, "path", env->path))
|
|
osync_member_set_slow_sync(env->member, "data", TRUE);
|
|
|
|
GError *direrror = NULL;
|
|
|
|
env->dir = g_dir_open(env->path, 0, &direrror);
|
|
if (direrror) {
|
|
//Unable to open directory
|
|
osync_context_report_error(ctx, OSYNC_ERROR_FILE_NOT_FOUND, "Unable to open directory %s", env->path);
|
|
g_error_free (direrror);
|
|
} else {
|
|
osync_context_report_success(ctx);
|
|
}
|
|
|
|
osync_trace(TRACE_EXIT, "%s", __func__);
|
|
}
|
|
|
|
static char *mock_generate_hash(struct stat *buf)
|
|
{
|
|
char *hash = g_strdup_printf("%i-%i", (int)buf->st_mtime, (int)buf->st_ctime);
|
|
return hash;
|
|
}
|
|
|
|
static void mock_get_changeinfo(OSyncContext *ctx)
|
|
{
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(ctx);
|
|
|
|
if (mock_get_error(env->member, "GET_CHANGES_ERROR")) {
|
|
osync_context_report_error(ctx, OSYNC_ERROR_EXPECTED, "Triggering GET_CHANGES_ERROR error");
|
|
return;
|
|
}
|
|
if (mock_get_error(env->member, "GET_CHANGES_TIMEOUT"))
|
|
return;
|
|
if (mock_get_error(env->member, "GET_CHANGES_TIMEOUT2"))
|
|
sleep(8);
|
|
|
|
if (osync_member_get_slow_sync(env->member, "data")) {
|
|
osync_debug("FILE-SYNC", 3, "Slow sync requested");
|
|
osync_hashtable_set_slow_sync(env->hashtable, "data");
|
|
}
|
|
|
|
GDir *dir;
|
|
GError *gerror = NULL;
|
|
const char *de = NULL;
|
|
|
|
dir = g_dir_open(env->path, 0, &gerror);
|
|
if (!dir) {
|
|
osync_trace(TRACE_EXIT_ERROR, "mock_report_dir: Unable to open directory %s: %s", env->path, gerror ? gerror->message : "None");
|
|
return;
|
|
}
|
|
while ((de = g_dir_read_name(dir))) {
|
|
char *filename = g_build_filename(env->path, de, NULL);
|
|
if (g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
|
|
/* Report normal files */
|
|
OSyncChange *change = osync_change_new();
|
|
osync_change_set_member(change, env->member);
|
|
osync_change_set_uid(change, de);
|
|
|
|
osync_change_set_objformat_string(change, "mockformat");
|
|
|
|
struct stat buf;
|
|
stat(filename, &buf);
|
|
char *hash = mock_generate_hash(&buf);
|
|
osync_change_set_hash(change, hash);
|
|
|
|
if (mock_get_error(env->member, "ONLY_INFO")) {
|
|
osync_change_set_data(change, NULL, 0, FALSE);
|
|
} else {
|
|
char *data = NULL;
|
|
int size = 0;
|
|
OSyncError *error = NULL;
|
|
if (!osync_file_read(filename, &data, &size, &error)) {
|
|
osync_context_report_osyncerror(ctx, &error);
|
|
g_free(filename);
|
|
return;
|
|
}
|
|
|
|
osync_change_set_data(change, data, size, TRUE);
|
|
}
|
|
|
|
if (mock_get_error(env->member, "SLOW_REPORT"))
|
|
sleep(1);
|
|
|
|
if (osync_hashtable_detect_change(env->hashtable, change)) {
|
|
osync_context_report_change(ctx, change);
|
|
osync_hashtable_update_hash(env->hashtable, change);
|
|
}
|
|
g_free(hash);
|
|
|
|
|
|
}
|
|
}
|
|
g_dir_close(dir);
|
|
osync_hashtable_report_deleted(env->hashtable, ctx, "data");
|
|
|
|
fail_unless(env->committed_all == TRUE, NULL);
|
|
env->committed_all = FALSE;
|
|
|
|
osync_context_report_success(ctx);
|
|
}
|
|
|
|
static void mock_get_data(OSyncContext *ctx, OSyncChange *change)
|
|
{
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(ctx);
|
|
|
|
if (mock_get_error(env->member, "GET_DATA_ERROR")) {
|
|
osync_context_report_error(ctx, OSYNC_ERROR_EXPECTED, "Triggering GET_DATA_ERROR error");
|
|
return;
|
|
}
|
|
if (mock_get_error(env->member, "GET_DATA_TIMEOUT"))
|
|
return;
|
|
|
|
char *filename = g_strdup_printf("%s/%s", env->path, osync_change_get_uid(change));
|
|
char *data = NULL;
|
|
int size = 0;
|
|
OSyncError *error = NULL;
|
|
if (!osync_file_read(filename, &data, &size, &error)) {
|
|
osync_context_report_osyncerror(ctx, &error);
|
|
g_free(filename);
|
|
return;
|
|
}
|
|
|
|
osync_change_set_data(change, data, size, TRUE);
|
|
g_free(filename);
|
|
|
|
osync_context_report_success(ctx);
|
|
}
|
|
|
|
static void mock_read(OSyncContext *ctx, OSyncChange *change)
|
|
{
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(ctx);
|
|
|
|
char *filename = g_strdup_printf("%s/%s", env->path, osync_change_get_uid(change));
|
|
|
|
char *data = NULL;
|
|
int size = 0;
|
|
OSyncError *error = NULL;
|
|
if (!osync_file_read(filename, &data, &size, &error)) {
|
|
osync_context_report_osyncerror(ctx, &error);
|
|
g_free(filename);
|
|
return;
|
|
}
|
|
|
|
osync_change_set_data(change, data, size, TRUE);
|
|
|
|
g_free(filename);
|
|
|
|
osync_context_report_success(ctx);
|
|
}
|
|
|
|
static osync_bool mock_access(OSyncContext *ctx, OSyncChange *change)
|
|
{
|
|
/*TODO: Create directory for file, if it doesn't exist */
|
|
osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, ctx, change);
|
|
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(ctx);
|
|
|
|
char *filename = NULL;
|
|
OSyncError *error = NULL;
|
|
filename = g_strdup_printf ("%s/%s", env->path, osync_change_get_uid(change));
|
|
|
|
switch (osync_change_get_changetype(change)) {
|
|
case CHANGE_DELETED:
|
|
if (!remove(filename) == 0) {
|
|
osync_debug("FILE-SYNC", 0, "Unable to remove file %s", filename);
|
|
osync_context_report_error(ctx, OSYNC_ERROR_FILE_NOT_FOUND, "Unable to write");
|
|
g_free(filename);
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to write", __func__);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case CHANGE_ADDED:
|
|
if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
|
|
osync_debug("FILE-SYNC", 0, "File %s already exists", filename);
|
|
osync_context_report_error(ctx, OSYNC_ERROR_EXISTS, "Entry already exists");
|
|
g_free(filename);
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: Entry already exists", __func__);
|
|
return FALSE;
|
|
}
|
|
/* No break. Continue below */
|
|
case CHANGE_MODIFIED:
|
|
//FIXME add permission and ownership for file-sync
|
|
if (!osync_file_write(filename, osync_change_get_data(change), osync_change_get_datasize(change), 0700, &error)) {
|
|
osync_debug("FILE-SYNC", 0, "Unable to write to file %s", filename);
|
|
osync_context_report_osyncerror(ctx, &error);
|
|
g_free(filename);
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
|
|
return FALSE;
|
|
}
|
|
|
|
struct stat buf;
|
|
stat(filename, &buf);
|
|
char *hash = mock_generate_hash(&buf);
|
|
osync_change_set_hash(change, hash);
|
|
break;
|
|
default:
|
|
fail("no changetype given");
|
|
}
|
|
g_free(filename);
|
|
|
|
osync_trace(TRACE_EXIT, "%s", __func__);
|
|
return TRUE;
|
|
}
|
|
|
|
static osync_bool mock_commit_change(OSyncContext *ctx, OSyncChange *change)
|
|
{
|
|
osync_debug("FILE-SYNC", 4, "start: %s", __func__);
|
|
osync_debug("FILE-SYNC", 3, "Writing change %s with changetype %i", osync_change_get_uid(change), osync_change_get_changetype(change));
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(ctx);
|
|
|
|
fail_unless(env->committed_all == FALSE, NULL);
|
|
|
|
if (mock_get_error(env->member, "COMMIT_ERROR")) {
|
|
osync_context_report_error(ctx, OSYNC_ERROR_EXPECTED, "Triggering COMMIT_ERROR error");
|
|
return FALSE;
|
|
}
|
|
if (mock_get_error(env->member, "COMMIT_TIMEOUT"))
|
|
return FALSE;
|
|
|
|
if (!mock_access(ctx, change))
|
|
return FALSE;
|
|
|
|
osync_hashtable_update_hash(env->hashtable, change);
|
|
osync_context_report_success(ctx);
|
|
osync_debug("FILE-SYNC", 4, "end: %s", __func__);
|
|
return TRUE;
|
|
}
|
|
|
|
static void mock_sync_done(OSyncContext *ctx)
|
|
{
|
|
osync_debug("FILE-SYNC", 3, "start: %s", __func__);
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(ctx);
|
|
|
|
if (mock_get_error(env->member, "SYNC_DONE_ERROR")) {
|
|
osync_context_report_error(ctx, OSYNC_ERROR_EXPECTED, "Triggering SYNC_DONE_ERROR error");
|
|
return;
|
|
}
|
|
if (mock_get_error(env->member, "SYNC_DONE_TIMEOUT"))
|
|
return;
|
|
|
|
osync_anchor_update(env->member, "path", env->path);
|
|
osync_context_report_success(ctx);
|
|
osync_debug("FILE-SYNC", 3, "end: %s", __func__);
|
|
}
|
|
|
|
static void mock_disconnect(OSyncContext *ctx)
|
|
{
|
|
osync_debug("FILE-SYNC", 3, "start: %s", __func__);
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(ctx);
|
|
|
|
if (!g_getenv("NO_COMMITTED_ALL_CHECK"))
|
|
fail_unless(env->committed_all == TRUE, NULL);
|
|
env->committed_all = FALSE;
|
|
|
|
if (mock_get_error(env->member, "DISCONNECT_ERROR")) {
|
|
osync_context_report_error(ctx, OSYNC_ERROR_EXPECTED, "Triggering DISCONNECT_ERROR error");
|
|
return;
|
|
}
|
|
if (mock_get_error(env->member, "DISCONNECT_TIMEOUT"))
|
|
return;
|
|
|
|
g_dir_close(env->dir);
|
|
osync_hashtable_close(env->hashtable);
|
|
osync_context_report_success(ctx);
|
|
osync_debug("FILE-SYNC", 3, "end: %s", __func__);
|
|
}
|
|
|
|
static void mock_finalize(void *data)
|
|
{
|
|
osync_debug("FILE-SYNC", 3, "start: %s", __func__);
|
|
mock_env *env = (mock_env *)data;
|
|
osync_hashtable_free(env->hashtable);
|
|
|
|
g_free(env->path);
|
|
g_free(env);
|
|
}
|
|
|
|
static osync_bool mock_is_available(OSyncError **error)
|
|
{
|
|
if (g_getenv("IS_NOT_AVAILABLE")) {
|
|
osync_error_set(error, OSYNC_ERROR_GENERIC, "file-sync plugin is not available");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void mock_batch_commit(OSyncContext *context, OSyncContext **contexts, OSyncChange **changes)
|
|
{
|
|
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, context, contexts, changes);
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(context);
|
|
|
|
fail_unless(env->committed_all == FALSE, NULL);
|
|
env->committed_all = TRUE;
|
|
|
|
int i;
|
|
for (i = 0; contexts[i]; i++) {
|
|
if (mock_access(contexts[i], changes[i])) {
|
|
osync_hashtable_update_hash(env->hashtable, changes[i]);
|
|
osync_context_report_success(contexts[i]);
|
|
}
|
|
}
|
|
|
|
if (g_getenv("NUM_BATCH_COMMITS")) {
|
|
int req = atoi(g_getenv("NUM_BATCH_COMMITS"));
|
|
fail_unless(req == i, NULL);
|
|
}
|
|
|
|
if (mock_get_error(env->member, "COMMITTED_ALL_ERROR")) {
|
|
osync_context_report_error(context, OSYNC_ERROR_EXPECTED, "Triggering COMMITTED_ALL_ERROR error");
|
|
return;
|
|
}
|
|
|
|
osync_context_report_success(context);
|
|
|
|
osync_trace(TRACE_EXIT, "%s", __func__);
|
|
}
|
|
|
|
static void mock_committed_all(OSyncContext *context)
|
|
{
|
|
osync_trace(TRACE_ENTRY, "%s(%p)", __func__, context);
|
|
mock_env *env = (mock_env *)osync_context_get_plugin_data(context);
|
|
|
|
fail_unless(env->committed_all == FALSE, NULL);
|
|
env->committed_all = TRUE;
|
|
|
|
if (mock_get_error(env->member, "COMMITTED_ALL_ERROR")) {
|
|
osync_context_report_error(context, OSYNC_ERROR_EXPECTED, "Triggering COMMITTED_ALL_ERROR error");
|
|
osync_trace(TRACE_EXIT_ERROR, "%s: Reporting error", __func__);
|
|
return;
|
|
}
|
|
|
|
osync_context_report_success(context);
|
|
|
|
osync_trace(TRACE_EXIT, "%s", __func__);
|
|
}
|
|
|
|
void get_info(OSyncEnv *env)
|
|
{
|
|
OSyncPluginInfo *info = osync_plugin_new_info(env);
|
|
|
|
info->name = "file-sync";
|
|
info->longname = "Mock Plugin";
|
|
info->description = "Mock Plugin";
|
|
info->version = 1;
|
|
|
|
info->functions.initialize = mock_initialize;
|
|
info->functions.connect = mock_connect;
|
|
info->functions.sync_done = mock_sync_done;
|
|
info->functions.disconnect = mock_disconnect;
|
|
info->functions.finalize = mock_finalize;
|
|
info->functions.get_changeinfo = mock_get_changeinfo;
|
|
info->functions.get_data = mock_get_data;
|
|
|
|
osync_plugin_accept_objtype(info, "data");
|
|
osync_plugin_accept_objformat(info, "data", "mockformat", NULL);
|
|
|
|
osync_plugin_set_access_objformat(info, "data", "mockformat", mock_access);
|
|
osync_plugin_set_read_objformat(info, "data", "mockformat", mock_read);
|
|
|
|
//Lets reduce the timeouts a bit so the checks work faster
|
|
info->timeouts.disconnect_timeout = 5;
|
|
info->timeouts.connect_timeout = 5;
|
|
info->timeouts.sync_done_timeout = 5;
|
|
info->timeouts.get_changeinfo_timeout = 5;
|
|
info->timeouts.get_data_timeout = 5;
|
|
info->timeouts.commit_timeout = 15;
|
|
|
|
|
|
if (g_getenv("NO_TIMEOUTS")) {
|
|
info->timeouts.disconnect_timeout = 0;
|
|
info->timeouts.connect_timeout = 0;
|
|
info->timeouts.sync_done_timeout = 0;
|
|
info->timeouts.get_changeinfo_timeout = 0;
|
|
info->timeouts.get_data_timeout = 0;
|
|
info->timeouts.commit_timeout = 0;
|
|
}
|
|
|
|
if (g_getenv("IS_AVAILABLE"))
|
|
info->functions.is_available = mock_is_available;
|
|
|
|
osync_plugin_set_batch_commit_objformat(info, "data", "mockformat", mock_batch_commit);
|
|
osync_plugin_set_commit_objformat(info, "data", "mockformat", mock_commit_change);
|
|
osync_plugin_set_committed_all_objformat(info, "data", "mockformat", mock_committed_all);
|
|
}
|