Add a read-only attribute to QofBook and check of same to

qof_instance_begin_edit. Add qof_session_safe_save. Add unit tests for 
these changes.

QofBook read-only attribute is to prevent starting an edit when the 
version of Gnucash which created a dataset is newer than the one 
currently running. This is to prevent writing records of an old format 
into an existing data set. The user can use File>Save As to create a new 
QofSession which will write a completely new dataset in the current 
format. This is an important safety feature for future versions of 
Gnucash which will not bring all of a dataset into memory, instead 
loading only what is immediately needed.

Safe-save is the Qof access to allow writing out a dataset to an 
existing server database (i.e., postgresql or mysql) while maintaining 
the ability to both transaction-protect saving individual records while 
maintaining the ability to rollback the entire save if something goes 
wrong.

The unit test framework is added to enable testing these components. 
More tests will be added in a later commit.



git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@20103 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
John Ralls 2011-01-14 23:25:27 +00:00
parent d8733bf204
commit 886da7638d
15 changed files with 377 additions and 4 deletions

View File

@ -1,3 +1,5 @@
SUBDIRS = test
lib_LTLIBRARIES = libgnc-qof.la
libgnc_qof_la_LDFLAGS= -version-info $(LIBQOF_LIBRARY_VERSION)

View File

@ -311,6 +311,7 @@ struct QofBackend_s
void (*run_query) (QofBackend *, gpointer);
void (*sync) (QofBackend *, /*@ dependent @*/ QofBook *);
void (*safe_sync) (QofBackend *, /*@ dependent @*/ QofBook *);
void (*load_config) (QofBackend *, KvpFrame *);
/*@ observer @*/
KvpFrame* (*get_config) (QofBackend *);

View File

@ -129,6 +129,7 @@ qof_backend_init(QofBackend *be)
be->run_query = NULL;
be->sync = NULL;
be->safe_sync = NULL;
be->load_config = NULL;
be->events_pending = NULL;

View File

@ -107,6 +107,7 @@ typedef enum
/* database errors */
ERR_SQL_MISSING_DATA = 3000, /**< database doesn't contain expected data */
ERR_SQL_DB_TOO_OLD, /**< database is old and needs upgrading */
ERR_SQL_DB_TOO_NEW, /**< database is newer, we can't write to it */
ERR_SQL_DB_BUSY, /**< database is busy, cannot upgrade version */
/* RPC errors */

View File

@ -76,6 +76,7 @@ qof_book_init (QofBook *book)
book->data_table_finalizers = g_hash_table_new (g_str_hash, g_str_equal);
book->book_open = 'y';
book->read_only = FALSE;
book->version = 0;
}
@ -301,6 +302,20 @@ qof_book_get_data (const QofBook *book, const char *key)
return g_hash_table_lookup (book->data_tables, (gpointer)key);
}
/* ====================================================================== */
gboolean
qof_book_is_readonly(const QofBook *book)
{
g_return_val_if_fail( book != NULL, TRUE );
return book->read_only;
}
void
qof_book_mark_readonly(QofBook *book)
{
g_return_if_fail( book != NULL );
book->read_only = TRUE;
}
/* ====================================================================== */
QofCollection *

View File

@ -96,6 +96,12 @@ struct _QofBook
/* Hash table of destroy callbacks for the data table. */
GHashTable *data_table_finalizers;
/* Boolean indicates whether book is safe to write to (true means
* that it isn't. The usual reason will be a database version
* mismatch with the running instance of Gnucash.
*/
gboolean read_only;
/* state flag: 'y' means 'open for editing',
* 'n' means 'book is closed'
* xxxxx shouldn't this be replaced by the instance editlevel ???
@ -221,6 +227,12 @@ void qof_book_set_data_fin (QofBook *book, const gchar *key, gpointer data,
/** Retrieves arbitrary pointers to structs stored by qof_book_set_data. */
gpointer qof_book_get_data (const QofBook *book, const gchar *key);
/** Return whether the book is read only. */
gboolean qof_book_is_readonly(const QofBook *book);
/** Mark the book as read only. */
void qof_book_mark_readonly(QofBook *book);
#endif /* SWIG */
/** Returns flag indicating whether this book uses trading accounts */

View File

@ -1055,6 +1055,7 @@ qof_begin_edit (QofInstance *inst)
if (!inst) return FALSE;
priv = GET_PRIVATE(inst);
if (qof_book_is_readonly(priv->book)) return FALSE;
priv->editlevel++;
if (1 < priv->editlevel) return FALSE;
if (0 >= priv->editlevel)

View File

@ -1250,9 +1250,10 @@ qof_session_load (QofSession *session,
*/
err = qof_session_get_error(session);
if ((err != ERR_BACKEND_NO_ERR) &&
(err != ERR_FILEIO_FILE_TOO_OLD) &&
(err != ERR_FILEIO_NO_ENCODING) &&
(err != ERR_SQL_DB_TOO_OLD))
(err != ERR_FILEIO_FILE_TOO_OLD) &&
(err != ERR_FILEIO_NO_ENCODING) &&
(err != ERR_SQL_DB_TOO_OLD) &&
(err != ERR_SQL_DB_TOO_NEW))
{
/* Something broke, put back the old stuff */
qof_book_set_backend (newbook, NULL);
@ -1262,7 +1263,6 @@ qof_session_load (QofSession *session,
LEAVE("error from backend %d", qof_session_get_error(session));
return;
}
for (node = oldbooks; node; node = node->next)
{
ob = node->data;
@ -1450,6 +1450,27 @@ leave:
return;
}
void
qof_session_safe_save(QofSession *session, QofPercentageFunc percentage_func)
{
QofBackend *be = session->backend;
gint err;
char *msg = NULL;
g_return_if_fail( be != NULL );
g_return_if_fail( be->safe_sync != NULL );
be->percentage = percentage_func;
(be->safe_sync)( be, qof_session_get_book( session ));
err = qof_backend_get_error(session->backend);
msg = qof_backend_get_message(session->backend);
if (err != ERR_BACKEND_NO_ERR)
{
g_free(session->book_id);
session->book_id = NULL;
qof_session_push_error (session, err, msg);
}
}
/* ====================================================================== */
gboolean
qof_session_save_in_progress(const QofSession *session)

View File

@ -234,6 +234,17 @@ gboolean qof_session_save_in_progress(const QofSession *session);
*/
void qof_session_save (QofSession *session,
QofPercentageFunc percentage_func);
/**
* A special version of save used in the sql backend which moves the
* existing tables aside, then saves everything to new tables, then
* deletes the old tables after the save is completed without
* error. If there are errors, it removes the old tables and renames
* the new tables back.
*/
void qof_session_safe_save (QofSession *session,
QofPercentageFunc percentage_func);
/**
* The qof_session_end() method will release the session lock. For the
* file backend, it will *not* save the data to a file. Thus,

View File

@ -0,0 +1,34 @@
# A template Makefile.am for GLib g_test-based test directories.
# Copyright 2011 John Ralls <jralls@ceridwen.us>
include $(top_srcdir)/test-templates/Makefile.decl
#You will only need one of these: It points to the module directory
#after $(top_srcdir) or $(top_builddir):
MODULEPATH = src/libqof/qof
test_qof_SOURCES = \
test-qof.c \
test-qofbook.c \
test-qofinstance.c \
test-qofsession.c
test_qof_HEADERSS = \
$(top_srcdir)/${MODULEPATH}/qofbook.h \
$(top_srcdir)/${MODULEPATH}/qofinstance.h \
$(top_srcdir)/${MODULEPATH/}qofsession.h
TEST_PROGS += test-qof
noinst_PROGRAMS = ${TEST_PROGS}
#The tests might require more libraries, but try to keep them
#as independent as possible.
test_qof_LDADD = ${top_builddir}/${MODULEPATH}/libgnc-qof.la
test_qof_CFLAGS = \
${DEFAULT_INCLUDES} \
-I$(top_srcdir)/${MODULEPATH}/ \
-DTESTPROG=test_qof \
${GLIB_CFLAGS}

View File

@ -0,0 +1,45 @@
/********************************************************************
* testmain.c: GLib g_test test execution file. *
* Copyright 2011 John Ralls <jralls@ceridwen.us> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include <glib.h>
extern void test_suite_qofbook();
extern void test_suite_qofinstance();
extern void test_suite_qofsession();
int
main (int argc,
char *argv[])
{
g_type_init(); /* Initialize the GObject system */
g_test_init ( &argc, &argv ); /* initialize test program */
qof_log_init_filename_special("/dev/null"); /* Init the log system */
test_suite_qofbook();
test_suite_qofinstance();
test_suite_qofsession();
return g_test_run( );
}

View File

@ -0,0 +1,31 @@
/********************************************************************
* test_qofbackend.c: GLib g_test test suite for qofbackend. *
* Copyright 2011 John Ralls <jralls@ceridwen.us> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include <glib/glib.h>
static const gchar *suitename = "qof/qofbackend";
GTestSuite*
test_suite_qofbackend ( void )
{
}

View File

@ -0,0 +1,59 @@
/********************************************************************
* test_qofbook.c: GLib g_test test suite for qofbook. *
* Copyright 2011 John Ralls <jralls@ceridwen.us> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include <config.h>
#include <string.h>
#include <glib.h>
#include <qof.h>
static const gchar *suitename = "/qof/qofbook";
typedef struct
{
QofBook *book;
} Fixture;
static void
setup( Fixture *fixture, gconstpointer pData )
{
fixture->book = qof_book_new();
}
static void
teardown( Fixture *fixture, gconstpointer pData )
{
qof_book_destroy( fixture->book );
}
static void
test_book_readonly( Fixture *fixture, gconstpointer pData )
{
g_assert( fixture->book != NULL );
g_assert( !qof_book_is_readonly( fixture->book ) );
qof_book_mark_readonly( fixture->book );
g_assert( qof_book_is_readonly( fixture->book ) );
}
void
test_suite_qofbook ( void )
{
g_test_add( suitename, Fixture, NULL, setup, test_book_readonly, teardown );
}

View File

@ -0,0 +1,63 @@
/********************************************************************
* test_qofinstance.c: GLib g_test test suite for qofinstance. *
* Copyright 2011 John Ralls <jralls@ceridwen.us> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include <config.h>
#include <glib.h>
#include <qof.h>
static const gchar *suitename = "/qof/qofinstance";
typedef struct
{
QofInstance *instance;
} Fixture;
static void
setup( Fixture *fixture, gconstpointer pData )
{
fixture->instance = g_object_new(QOF_TYPE_INSTANCE, NULL);
qof_instance_set_book( fixture->instance, qof_book_new() );
}
static void
teardown( Fixture *fixture, gconstpointer pData )
{
qof_book_destroy( qof_instance_get_book( fixture->instance ) );
g_object_unref(fixture->instance);
}
static void
test_book_readonly( Fixture *fixture, gconstpointer pData )
{
QofBook *book = qof_instance_get_book( fixture->instance );
g_assert( !qof_book_is_readonly( book ) );
qof_book_mark_readonly( book );
g_assert( qof_book_is_readonly( book ) );
g_assert( !qof_begin_edit( fixture->instance ) );
}
void
test_suite_qofinstance ( void )
{
g_test_add( suitename, Fixture, NULL, setup, test_book_readonly, teardown );
}

View File

@ -0,0 +1,76 @@
/********************************************************************
* test_qofsession.c: GLib g_test test suite for qofsession. *
* Copyright 2011 John Ralls <jralls@ceridwen.us> *
* *
* 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include <config.h>
#include <glib.h>
#include <qof.h>
#include <qofbackend-p.h>
#include <qofsession-p.h>
static const gchar *suitename = "/qof/qofsession";
typedef struct
{
QofSession *session;
} Fixture;
static void
safe_sync( QofBackend *be, QofBook *book )
{
qof_backend_set_error( be, ERR_BACKEND_DATA_CORRUPT );
qof_backend_set_message( be, "Just Kidding!" );
}
static void
percentage_fn ( const char* message, double percent )
{
g_print( "%s %f complete", message, percent );
}
static void
setup( Fixture *fixture, gconstpointer pData )
{
fixture->session = qof_session_new();
fixture->session->backend = g_new0( QofBackend, 1 );
fixture->session->backend->safe_sync = safe_sync;
}
static void
teardown( Fixture *fixture, gconstpointer pData )
{
qof_session_destroy( fixture->session );
}
static void
test_session_safe_save( Fixture *fixture, gconstpointer pData )
{
qof_session_safe_save( fixture->session, percentage_fn );
g_assert_cmpint( ERR_BACKEND_DATA_CORRUPT, ==,
qof_session_get_error( fixture->session ));
g_assert( NULL == qof_session_get_url( fixture->session ));
}
GTestSuite*
test_suite_qofsession ( void )
{
g_test_add( suitename, Fixture, NULL, setup, test_session_safe_save, teardown );
}