Test loading and saving XML files with and without compression

Add new test files that are written with the latest version of GnuCash.
Test loading them, saving to compressed files, loading those compressed
files and writing to an uncompressed file. At every stage check it matches
the canonical version.
This commit is contained in:
Simon Arlott 2023-06-27 19:08:13 +01:00
parent ab4e595670
commit 35bee1382d
No known key found for this signature in database
GPG Key ID: DF001BFD83E75990
8 changed files with 2330 additions and 10 deletions

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