Merge Simon Arlott's 'test-xml-save' into stable.

This commit is contained in:
John Ralls 2023-08-03 11:05:11 -07:00
commit 4d46a8191d
9 changed files with 2332 additions and 10 deletions

View File

@ -133,6 +133,8 @@ void failure_args(
#define do_test( result, title ) do_test_call( result, title, __FILE__, __LINE__ )
#define success( title ) success_call( title, __FILE__, __LINE__ );
#define failure( title ) failure_call( title, __FILE__, __LINE__ );
#define failuref( title, format, ... ) failure_args( title, __FILE__, __LINE__, format, ## __VA_ARGS__ );
/** This one doesn't work because macros can't take a variable number of arguments.
* well, apparently gcc can, but it's non-standard.

View File

@ -14,12 +14,17 @@ set(XML_TEST_INCLUDE_DIRS
set(XML_TEST_LIBS gnc-engine gnc-test-engine test-core ${LIBXML2_LDFLAGS} -lz)
set(XML_GTEST_LIBS ${XML_TEST_LIBS} gtest)
function(add_xml_test _TARGET _SOURCE_FILES)
gnc_add_test(${_TARGET} "${_SOURCE_FILES}" XML_TEST_INCLUDE_DIRS XML_TEST_LIBS ${ARGN})
target_compile_options(${_TARGET} PRIVATE -DU_SHOW_CPLUSPLUS_API=0 -DG_LOG_DOMAIN=\"gnc.backend.xml\")
endfunction()
function(add_xml_gtest _TARGET _SOURCE_FILES)
gnc_add_test(${_TARGET} "${_SOURCE_FILES}" XML_TEST_INCLUDE_DIRS XML_GTEST_LIBS ${ARGN})
target_compile_options(${_TARGET} PRIVATE -DU_SHOW_CPLUSPLUS_API=0 -DG_LOG_DOMAIN=\"gnc.backend.xml\")
endfunction()
################################
@ -55,13 +60,28 @@ set(test_backend_xml_module_SOURCES
${CMAKE_SOURCE_DIR}/libgnucash/backend/xml/gnc-pricedb-xml-v2.cpp
)
set_local_dist(test_backend_xml_DIST_local CMakeLists.txt grab-types.pl
README test-dom-converters1.cpp
test-dom-parser1.cpp test-file-stuff.cpp test-file-stuff.h test-kvp-frames.cpp
test-load-backend.cpp test-load-example-account.cpp test-load-xml2.cpp
test-save-in-lang.cpp test-string-converters.cpp test-xml2-is-file.cpp
test-xml-account.cpp test-real-data.sh test-xml-commodity.cpp
test-xml-pricedb.cpp test-xml-transaction.cpp)
set_local_dist(test_backend_xml_DIST_local
CMakeLists.txt
grab-types.pl
README
test-dom-converters1.cpp
test-dom-parser1.cpp
test-file-stuff.cpp
test-file-stuff.h
test-kvp-frames.cpp
test-load-backend.cpp
test-load-example-account.cpp
test-load-save-files.cpp
test-load-xml2.cpp
test-real-data.sh
test-save-in-lang.cpp
test-string-converters.cpp
test-xml2-is-file.cpp
test-xml-account.cpp
test-xml-commodity.cpp
test-xml-pricedb.cpp
test-xml-transaction.cpp
)
set(test_backend_xml_DIST ${test_backend_xml_DIST_local} ${test_backend_xml_test_files_DIST} PARENT_SCOPE)
add_xml_test(test-dom-converters1 "${test_backend_xml_base_SOURCES};test-dom-converters1.cpp")
@ -80,6 +100,9 @@ add_xml_test(test-load-example-account
GNC_ACCOUNT_PATH=${CMAKE_SOURCE_DIR}/data/accounts/C
)
target_compile_options(test-load-example-account PRIVATE -DU_SHOW_CPLUSPLUS_API=0)
add_xml_gtest(test-load-save-files gtest-load-save-files.cpp
GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files/load-save
)
add_xml_test(test-string-converters "${test_backend_xml_base_SOURCES};test-string-converters.cpp")
add_xml_test(test-xml-account "${test_backend_xml_module_SOURCES};test-xml-account.cpp;test-file-stuff.cpp")
add_xml_test(test-xml-commodity "${test_backend_xml_module_SOURCES};test-xml-commodity.cpp;test-file-stuff.cpp")

View File

@ -6,6 +6,7 @@ TESTS:
test-dom-converters1.c: test each simple dom converter
test-dom-parser1.c: unused now
test-kvp-frames.c: test the kvp frame dom generators and parsers
test-load-save-files.c: test loading and saving compressed and uncompressed files
test-load-xml2.c: test the larger xml loading
test-save-in-lang.c: incomplete test to test saving in different LANGs
test-string-converters.c: test some string converters
@ -15,4 +16,3 @@ test-xml-transaction.c: ditto Transaction's
test-xml2-is-file.c: test the is_file function
test-real-data.sh: run the test-xml-{account,commodity,transaction} programs
on real data rather than random data

View File

@ -0,0 +1,318 @@
/********************************************************************\
* 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>
#include <glib/gfileutils.h>
#include <glib/gstdio.h>
#include <config.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <zlib.h>
#include <cstdlib>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <cashobjects.h>
#include <TransLog.h>
#include <gnc-engine.h>
#include <gnc-prefs.h>
#include <gtest/gtest.h>
#include <unittest-support.h>
#include "../gnc-backend-xml.h"
#include "../io-gncxml-v2.h"
#define GNC_LIB_NAME "gncmod-backend-xml"
#define GNC_LIB_REL_PATH "xml"
static std::vector<unsigned char> read_file (std::string filename)
{
gchar *contents;
gsize length = 0;
if (g_file_get_contents (filename.c_str (), &contents, &length, nullptr))
{
std::vector<unsigned char> data(length);
memcpy (data.data (), contents, length);
g_free (contents);
return data;
}
else
{
return {};
}
}
static bool
compare_files (std::string filename1, std::string filename2)
{
auto contents1 = read_file (filename1);
auto contents2 = read_file (filename2);
if (contents1.size () > 0 && contents1.size () == contents2.size ()
&& !memcmp(contents1.data (), contents2.data (), contents1.size ())) {
return true;
} else {
ADD_FAILURE() << "compare_files: " << filename1 << " and " << filename2 << " are different";
return false;
}
}
static bool
decompress_file (std::string filename, const std::vector<unsigned char> &in, std::vector<unsigned char> &out)
{
/* 037 0213 are the header id bytes for a gzipped file. */
if (in.size () < 2 || in[0] != 037 || in[1] != 0213)
{
ADD_FAILURE() << "decompress_file: " << filename << " is not compressed";
return false;
}
z_stream stream{};
stream.next_in = const_cast<unsigned char*>(in.data ());
stream.avail_in = in.size ();
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.next_out = out.data ();
stream.avail_out = out.size ();
/* "add 16 to decode only the gzip format" */
int ret = inflateInit2 (&stream, 16 + MAX_WBITS);
if (ret != Z_OK)
{
ADD_FAILURE() << "decompress_file: " << filename << " could not be uncompressed (inflateInit): " << ret;
return false;
}
ret = inflate (&stream, Z_NO_FLUSH);
if (ret != Z_STREAM_END)
{
ADD_FAILURE() << "decompress_file: " << filename << " could not be uncompressed (inflate "
<< in.size () << " into " << out.size() << "): " << ret << " " << stream.msg
<< " (avail_in " << stream.avail_in << " avail_out " << stream.avail_out << ")";
inflateEnd (&stream);
return false;
}
ret = inflateEnd (&stream);
if (ret != Z_OK)
{
ADD_FAILURE() << "decompress_file: " << filename << " could not be uncompressed (inflateEnd): " << ret;
return false;
}
if (stream.avail_in)
{
ADD_FAILURE() << "decompress_file: " << filename << " has unused compressed data: " << stream.avail_in;
return false;
}
out.resize (stream.avail_out);
return true;
}
static bool
compare_compressed_files (std::string uncompressed_filename1, std::string compressed_filename2)
{
auto uncompressed_contents1 = read_file (uncompressed_filename1);
auto compressed_contents2 = read_file (compressed_filename2);
/* Allow some space to grow beyond the expected size */
std::vector<unsigned char> uncompressed_contents2(uncompressed_contents1.size () * 2);
if (!decompress_file (compressed_filename2, compressed_contents2, uncompressed_contents2))
return false;
if (uncompressed_contents1.size () > 0
&& uncompressed_contents1.size () != uncompressed_contents2.size ())
{
ADD_FAILURE() << "compare_compressed_files: " << uncompressed_filename1
<< " and " << compressed_filename2 << " are different sizes or empty ("
<< uncompressed_contents1.size () << " and "
<< uncompressed_contents2.size () << ")";
return false;
}
if (!memcmp(uncompressed_contents1.data (), uncompressed_contents2.data (), uncompressed_contents1.size ())) {
return true;
} else {
ADD_FAILURE() << "compare_compressed_files: " << uncompressed_filename1
<< " and " << compressed_filename2 << " are different";
return false;
}
}
/* The original file is used for comparisons. The file will be different when
* there are future changes in the GnuCash output and needs to be updated if
* that happens.
*
* Using the same file each time also checks that nothing in the file will swap
* between two stable states (bug 746937).
*/
class LoadSaveFiles : public testing::TestWithParam<std::string>
{
public:
static void SetUpTestSuite ()
{
g_setenv ("GNC_UNINSTALLED", "1", TRUE);
qof_init ();
cashobjects_register ();
ASSERT_TRUE(qof_load_backend_library (GNC_LIB_REL_PATH, GNC_LIB_NAME)) << "loading gnc-backend-xml GModule failed";
xaccLogDisable ();
}
static void TearDownTestSuite ()
{
qof_close ();
}
};
#define QOF_SESSION_CHECKED_CALL(_function, _session, ...) \
do { \
_function (_session.get (), ## __VA_ARGS__); \
ASSERT_EQ (qof_session_get_error (_session.get ()), 0) << #_function \
<< " (" << #_session << ".get (), " << #__VA_ARGS__ << "): " << qof_session_get_error (_session.get ()) \
<< " \"" << qof_session_get_error_message (_session.get ()) << "\""; \
} while (0)
TEST_P(LoadSaveFiles, test_file)
{
auto filename = GetParam();
/* Verify that we can write a compressed version of the original file that
* has the original content when uncompressed.
*/
auto new_compressed_file = filename + "-test-compressed~";
/* Verify that we can read a compressed file and write an uncompressed file
* that has the original content.
*/
auto new_uncompressed_file = filename + "-test-uncompressed~";
const char *logdomain = "backend.xml";
GLogLevelFlags loglevel = static_cast<decltype (loglevel)>
(G_LOG_LEVEL_WARNING);
TestErrorStruct check = { loglevel, const_cast<char*> (logdomain), nullptr };
g_log_set_handler (logdomain, loglevel,
(GLogFunc)test_checked_handler, &check);
{
auto load_uncompressed_session = std::shared_ptr<QofSession>{qof_session_new (qof_book_new ()), qof_session_destroy};
QOF_SESSION_CHECKED_CALL(qof_session_begin, load_uncompressed_session, filename.c_str (), SESSION_READ_ONLY);
QOF_SESSION_CHECKED_CALL(qof_session_load, load_uncompressed_session, nullptr);
auto save_compressed_session = std::shared_ptr<QofSession>{qof_session_new (nullptr), qof_session_destroy};
g_unlink (new_compressed_file.c_str ());
g_unlink ((new_compressed_file + ".LCK").c_str ());
QOF_SESSION_CHECKED_CALL(qof_session_begin, save_compressed_session, new_compressed_file.c_str (), SESSION_NEW_OVERWRITE);
qof_event_suspend ();
qof_session_swap_data (load_uncompressed_session.get (), save_compressed_session.get ());
qof_book_mark_session_dirty (qof_session_get_book (save_compressed_session.get ()));
qof_event_resume ();
qof_session_end (load_uncompressed_session.get ());
gnc_prefs_set_file_save_compressed (TRUE);
QOF_SESSION_CHECKED_CALL(qof_session_save, save_compressed_session, nullptr);
qof_session_end (save_compressed_session.get ());
}
if (!compare_compressed_files (filename, new_compressed_file))
return;
{
auto load_compressed_session = std::shared_ptr<QofSession>{qof_session_new (qof_book_new ()), qof_session_destroy};
QOF_SESSION_CHECKED_CALL(qof_session_begin, load_compressed_session, new_compressed_file.c_str (), SESSION_READ_ONLY);
QOF_SESSION_CHECKED_CALL(qof_session_load, load_compressed_session, nullptr);
auto save_uncompressed_session = std::shared_ptr<QofSession>{qof_session_new (nullptr), qof_session_destroy};
g_unlink (new_uncompressed_file.c_str ());
g_unlink ((new_uncompressed_file + ".LCK").c_str ());
QOF_SESSION_CHECKED_CALL(qof_session_begin, save_uncompressed_session, new_uncompressed_file.c_str (), SESSION_NEW_OVERWRITE);
qof_event_suspend ();
qof_session_swap_data (load_compressed_session.get (), save_uncompressed_session.get ());
qof_book_mark_session_dirty (qof_session_get_book (save_uncompressed_session.get ()));
qof_event_resume ();
qof_session_end (load_compressed_session.get ());
gnc_prefs_set_file_save_compressed (FALSE);
QOF_SESSION_CHECKED_CALL(qof_session_save, save_uncompressed_session, nullptr);
qof_session_end (save_uncompressed_session.get ());
}
if (!compare_files (filename, new_uncompressed_file))
return;
}
std::vector<std::string> ListTestCases ();
INSTANTIATE_TEST_SUITE_P(
LoadSaveFilesDir,
LoadSaveFiles,
testing::ValuesIn (ListTestCases ()));
std::vector<std::string> ListTestCases ()
{
std::vector<std::string> files;
const char *location = g_getenv ("GNC_TEST_FILES");
if (!location)
location = "test-files/load-save";
std::shared_ptr<GDir> dir{g_dir_open (location, 0, nullptr), g_dir_close};
if (dir)
{
const gchar *entry;
while ((entry = g_dir_read_name (dir.get ())) != nullptr)
{
if (g_str_has_suffix (entry, ".gnucash"))
{
std::shared_ptr<gchar> to_open{g_build_filename (location, entry, (gchar*)nullptr), g_free};
if (!g_file_test (to_open.get (), G_FILE_TEST_IS_DIR))
files.push_back (to_open.get());
}
}
}
else
{
ADD_FAILURE() << "unable to open directory " << location;
}
EXPECT_FALSE(files.empty()) << "no files found in " << location;
return files;
}

View File

@ -1,6 +1,6 @@
add_subdirectory(load-save)
add_subdirectory(xml2)
set_local_dist(test_backend_xml_test_files_DIST_local CMakeLists.txt )
set(test_backend_xml_test_files_DIST ${test_backend_xml_test_files_DIST_local} ${xml2_DIST} PARENT_SCOPE)
set(test_backend_xml_test_files_DIST ${test_backend_xml_test_files_DIST_local} ${load_save_DIST} ${xml2_DIST} PARENT_SCOPE)

View File

@ -0,0 +1,4 @@
set_dist_list(load_save_DIST
bug-746937-sx-split-order.gnucash
sample1.gnucash
)

View File

@ -0,0 +1,6 @@
These files are used for testing loading and saving
If the "load-save-files" test fails because the written XML file is now
different, zcat *.gnucash-compressed~ to *.gnucash, check that the changes
are appropriate, expected and have the right backward/forward compatibility
then commit the new versions of the files.

View File

@ -0,0 +1,406 @@
<?xml version="1.0" encoding="utf-8" ?>
<gnc-v2
xmlns:gnc="http://www.gnucash.org/XML/gnc"
xmlns:act="http://www.gnucash.org/XML/act"
xmlns:book="http://www.gnucash.org/XML/book"
xmlns:cd="http://www.gnucash.org/XML/cd"
xmlns:cmdty="http://www.gnucash.org/XML/cmdty"
xmlns:price="http://www.gnucash.org/XML/price"
xmlns:slot="http://www.gnucash.org/XML/slot"
xmlns:split="http://www.gnucash.org/XML/split"
xmlns:sx="http://www.gnucash.org/XML/sx"
xmlns:trn="http://www.gnucash.org/XML/trn"
xmlns:ts="http://www.gnucash.org/XML/ts"
xmlns:fs="http://www.gnucash.org/XML/fs"
xmlns:bgt="http://www.gnucash.org/XML/bgt"
xmlns:recurrence="http://www.gnucash.org/XML/recurrence"
xmlns:lot="http://www.gnucash.org/XML/lot"
xmlns:addr="http://www.gnucash.org/XML/addr"
xmlns:billterm="http://www.gnucash.org/XML/billterm"
xmlns:bt-days="http://www.gnucash.org/XML/bt-days"
xmlns:bt-prox="http://www.gnucash.org/XML/bt-prox"
xmlns:cust="http://www.gnucash.org/XML/cust"
xmlns:employee="http://www.gnucash.org/XML/employee"
xmlns:entry="http://www.gnucash.org/XML/entry"
xmlns:invoice="http://www.gnucash.org/XML/invoice"
xmlns:job="http://www.gnucash.org/XML/job"
xmlns:order="http://www.gnucash.org/XML/order"
xmlns:owner="http://www.gnucash.org/XML/owner"
xmlns:taxtable="http://www.gnucash.org/XML/taxtable"
xmlns:tte="http://www.gnucash.org/XML/tte"
xmlns:vendor="http://www.gnucash.org/XML/vendor">
<gnc:count-data cd:type="book">1</gnc:count-data>
<gnc:book version="2.0.0">
<book:id type="guid">78552b4ebbba9bf429f6a1f4b861b538</book:id>
<gnc:count-data cd:type="commodity">1</gnc:count-data>
<gnc:count-data cd:type="account">9</gnc:count-data>
<gnc:count-data cd:type="schedxaction">1</gnc:count-data>
<gnc:commodity version="2.0.0">
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
<cmdty:get_quotes/>
<cmdty:quote_source>currency</cmdty:quote_source>
<cmdty:quote_tz/>
</gnc:commodity>
<gnc:commodity version="2.0.0">
<cmdty:space>template</cmdty:space>
<cmdty:id>template</cmdty:id>
<cmdty:name>template</cmdty:name>
<cmdty:xcode>template</cmdty:xcode>
<cmdty:fraction>1</cmdty:fraction>
</gnc:commodity>
<gnc:account version="2.0.0">
<act:name>Root Account</act:name>
<act:id type="guid">22067e5b3cf257e20070e0d2307ac0b0</act:id>
<act:type>ROOT</act:type>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Assets</act:name>
<act:id type="guid">1244bef69b14889a06271df59645c35d</act:id>
<act:type>ASSET</act:type>
<act:commodity>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</act:commodity>
<act:commodity-scu>100</act:commodity-scu>
<act:description>Assets</act:description>
<act:slots>
<slot>
<slot:key>placeholder</slot:key>
<slot:value type="string">true</slot:value>
</slot>
</act:slots>
<act:parent type="guid">22067e5b3cf257e20070e0d2307ac0b0</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Current Assets</act:name>
<act:id type="guid">be6deddf8a970ff14cb8be91a958a3eb</act:id>
<act:type>ASSET</act:type>
<act:commodity>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</act:commodity>
<act:commodity-scu>100</act:commodity-scu>
<act:description>Current Assets</act:description>
<act:slots>
<slot>
<slot:key>placeholder</slot:key>
<slot:value type="string">true</slot:value>
</slot>
</act:slots>
<act:parent type="guid">1244bef69b14889a06271df59645c35d</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Bank</act:name>
<act:id type="guid">9b0050507226d570c754f688caf3e577</act:id>
<act:type>BANK</act:type>
<act:commodity>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</act:commodity>
<act:commodity-scu>100</act:commodity-scu>
<act:slots>
<slot>
<slot:key>placeholder</slot:key>
<slot:value type="string">true</slot:value>
</slot>
</act:slots>
<act:parent type="guid">be6deddf8a970ff14cb8be91a958a3eb</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Current Account</act:name>
<act:id type="guid">f0d5a52f1fd957b8a414aedf3f55c626</act:id>
<act:type>BANK</act:type>
<act:commodity>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</act:commodity>
<act:commodity-scu>100</act:commodity-scu>
<act:parent type="guid">9b0050507226d570c754f688caf3e577</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Liabilities</act:name>
<act:id type="guid">1f5a3d5c6157c5e78d9961bbd3afc85f</act:id>
<act:type>LIABILITY</act:type>
<act:commodity>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</act:commodity>
<act:commodity-scu>100</act:commodity-scu>
<act:description>Liabilities</act:description>
<act:slots>
<slot>
<slot:key>placeholder</slot:key>
<slot:value type="string">true</slot:value>
</slot>
</act:slots>
<act:parent type="guid">22067e5b3cf257e20070e0d2307ac0b0</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Company</act:name>
<act:id type="guid">59b2fcdab565c906362f5a24f4a909f7</act:id>
<act:type>LIABILITY</act:type>
<act:commodity>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</act:commodity>
<act:commodity-scu>100</act:commodity-scu>
<act:parent type="guid">1f5a3d5c6157c5e78d9961bbd3afc85f</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Account 1</act:name>
<act:id type="guid">b5c34f61b594d4e6c8ce18591a9813ad</act:id>
<act:type>LIABILITY</act:type>
<act:commodity>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</act:commodity>
<act:commodity-scu>100</act:commodity-scu>
<act:parent type="guid">59b2fcdab565c906362f5a24f4a909f7</act:parent>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>Account 2</act:name>
<act:id type="guid">bce28fe18ad558edb87c8282369ed373</act:id>
<act:type>LIABILITY</act:type>
<act:commodity>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</act:commodity>
<act:commodity-scu>100</act:commodity-scu>
<act:slots>
<slot>
<slot:key>color</slot:key>
<slot:value type="string">Not Set</slot:value>
</slot>
</act:slots>
<act:parent type="guid">59b2fcdab565c906362f5a24f4a909f7</act:parent>
</gnc:account>
<gnc:template-transactions>
<gnc:account version="2.0.0">
<act:name>Template Root</act:name>
<act:id type="guid">4586cdb4267a59437526549e14b0e74b</act:id>
<act:type>ROOT</act:type>
<act:commodity>
<cmdty:space>template</cmdty:space>
<cmdty:id>template</cmdty:id>
</act:commodity>
<act:commodity-scu>1</act:commodity-scu>
<act:non-standard-scu/>
</gnc:account>
<gnc:account version="2.0.0">
<act:name>52f96837087e9670c933f9421b0ac626</act:name>
<act:id type="guid">22f2905cbcfa0b910c0dbc348cfc1161</act:id>
<act:type>BANK</act:type>
<act:commodity>
<cmdty:space>template</cmdty:space>
<cmdty:id>template</cmdty:id>
</act:commodity>
<act:commodity-scu>1</act:commodity-scu>
<act:non-standard-scu/>
<act:parent type="guid">4586cdb4267a59437526549e14b0e74b</act:parent>
</gnc:account>
<gnc:transaction version="2.0.0">
<trn:id type="guid">68bf5e7740392eb15a079ee99f18a357</trn:id>
<trn:currency>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</trn:currency>
<trn:date-posted>
<ts:date>2010-08-01 08:40:17 +0000</ts:date>
</trn:date-posted>
<trn:date-entered>
<ts:date>2010-08-01 08:40:17 +0000</ts:date>
</trn:date-entered>
<trn:description>Description 2</trn:description>
<trn:splits>
<trn:split>
<split:id type="guid">676a518eb1cf05ca552434533288a5cd</split:id>
<split:reconciled-state>n</split:reconciled-state>
<split:value>0/100</split:value>
<split:quantity>0/1</split:quantity>
<split:account type="guid">22f2905cbcfa0b910c0dbc348cfc1161</split:account>
<split:slots>
<slot>
<slot:key>sched-xaction</slot:key>
<slot:value type="frame">
<slot>
<slot:key>account</slot:key>
<slot:value type="guid">bce28fe18ad558edb87c8282369ed373</slot:value>
</slot>
<slot>
<slot:key>credit-formula</slot:key>
<slot:value type="string"></slot:value>
</slot>
<slot>
<slot:key>credit-numeric</slot:key>
<slot:value type="numeric">0/1</slot:value>
</slot>
<slot>
<slot:key>debit-formula</slot:key>
<slot:value type="string">456</slot:value>
</slot>
<slot>
<slot:key>debit-numeric</slot:key>
<slot:value type="numeric">456/1</slot:value>
</slot>
</slot:value>
</slot>
</split:slots>
</trn:split>
<trn:split>
<split:id type="guid">1b779b05012759adbac315f1f50af621</split:id>
<split:reconciled-state>n</split:reconciled-state>
<split:value>0/100</split:value>
<split:quantity>0/1</split:quantity>
<split:account type="guid">22f2905cbcfa0b910c0dbc348cfc1161</split:account>
<split:slots>
<slot>
<slot:key>sched-xaction</slot:key>
<slot:value type="frame">
<slot>
<slot:key>account</slot:key>
<slot:value type="guid">f0d5a52f1fd957b8a414aedf3f55c626</slot:value>
</slot>
<slot>
<slot:key>credit-formula</slot:key>
<slot:value type="string">456</slot:value>
</slot>
<slot>
<slot:key>credit-numeric</slot:key>
<slot:value type="numeric">456/1</slot:value>
</slot>
<slot>
<slot:key>debit-formula</slot:key>
<slot:value type="string"></slot:value>
</slot>
<slot>
<slot:key>debit-numeric</slot:key>
<slot:value type="numeric">0/1</slot:value>
</slot>
</slot:value>
</slot>
</split:slots>
</trn:split>
</trn:splits>
</gnc:transaction>
<gnc:transaction version="2.0.0">
<trn:id type="guid">2fbcf6cc8224673bc93501399d4ca7be</trn:id>
<trn:currency>
<cmdty:space>CURRENCY</cmdty:space>
<cmdty:id>GBP</cmdty:id>
</trn:currency>
<trn:date-posted>
<ts:date>2010-09-11 23:00:00 +0000</ts:date>
</trn:date-posted>
<trn:date-entered>
<ts:date>2010-09-12 08:51:53 +0000</ts:date>
</trn:date-entered>
<trn:description>Description 1</trn:description>
<trn:slots>
<slot>
<slot:key>notes</slot:key>
<slot:value type="string"></slot:value>
</slot>
</trn:slots>
<trn:splits>
<trn:split>
<split:id type="guid">378dd92bd1819a52e491b6d8c123f662</split:id>
<split:reconciled-state>n</split:reconciled-state>
<split:value>0/100</split:value>
<split:quantity>0/1</split:quantity>
<split:account type="guid">22f2905cbcfa0b910c0dbc348cfc1161</split:account>
<split:slots>
<slot>
<slot:key>sched-xaction</slot:key>
<slot:value type="frame">
<slot>
<slot:key>account</slot:key>
<slot:value type="guid">b5c34f61b594d4e6c8ce18591a9813ad</slot:value>
</slot>
<slot>
<slot:key>credit-formula</slot:key>
<slot:value type="string"></slot:value>
</slot>
<slot>
<slot:key>credit-numeric</slot:key>
<slot:value type="numeric">0/1</slot:value>
</slot>
<slot>
<slot:key>debit-formula</slot:key>
<slot:value type="string">123</slot:value>
</slot>
<slot>
<slot:key>debit-numeric</slot:key>
<slot:value type="numeric">123/1</slot:value>
</slot>
</slot:value>
</slot>
</split:slots>
</trn:split>
<trn:split>
<split:id type="guid">75e005248499f7dfb46a80c9778e2e13</split:id>
<split:reconciled-state>n</split:reconciled-state>
<split:value>0/100</split:value>
<split:quantity>0/1</split:quantity>
<split:account type="guid">22f2905cbcfa0b910c0dbc348cfc1161</split:account>
<split:slots>
<slot>
<slot:key>sched-xaction</slot:key>
<slot:value type="frame">
<slot>
<slot:key>account</slot:key>
<slot:value type="guid">f0d5a52f1fd957b8a414aedf3f55c626</slot:value>
</slot>
<slot>
<slot:key>credit-formula</slot:key>
<slot:value type="string">123</slot:value>
</slot>
<slot>
<slot:key>credit-numeric</slot:key>
<slot:value type="numeric">123/1</slot:value>
</slot>
<slot>
<slot:key>debit-formula</slot:key>
<slot:value type="string"></slot:value>
</slot>
<slot>
<slot:key>debit-numeric</slot:key>
<slot:value type="numeric">0/1</slot:value>
</slot>
</slot:value>
</slot>
</split:slots>
</trn:split>
</trn:splits>
</gnc:transaction>
</gnc:template-transactions>
<gnc:schedxaction version="2.0.0">
<sx:id type="guid">52f96837087e9670c933f9421b0ac626</sx:id>
<sx:name>Scheduled Transaction</sx:name>
<sx:enabled>n</sx:enabled>
<sx:autoCreate>y</sx:autoCreate>
<sx:autoCreateNotify>n</sx:autoCreateNotify>
<sx:advanceCreateDays>90</sx:advanceCreateDays>
<sx:advanceRemindDays>0</sx:advanceRemindDays>
<sx:instanceCount>5</sx:instanceCount>
<sx:start>
<gdate>2010-10-05</gdate>
</sx:start>
<sx:last>
<gdate>2011-11-07</gdate>
</sx:last>
<sx:templ-acct type="guid">22f2905cbcfa0b910c0dbc348cfc1161</sx:templ-acct>
<sx:schedule>
<gnc:recurrence version="1.0.0">
<recurrence:mult>1</recurrence:mult>
<recurrence:period_type>month</recurrence:period_type>
<recurrence:start>
<gdate>2010-10-05</gdate>
</recurrence:start>
<recurrence:weekend_adj>forward</recurrence:weekend_adj>
</gnc:recurrence>
</sx:schedule>
</gnc:schedxaction>
</gnc:book>
</gnc-v2>

File diff suppressed because it is too large Load Diff