From e584704e7be0e2f77d8815137395cc3ea7d089b2 Mon Sep 17 00:00:00 2001 From: Paul Culley Date: Tue, 6 Dec 2016 11:17:54 +1100 Subject: [PATCH] Update Merge tests to actually run So this was a lot of work... Updated to use lxml, steep learning curve for someone who never examined XML before... The merge test code needed some updates because it was last used with an older version of the Gramps XML. Found another bug in the mergefamilyquery code when test started running; another nonetype issue. Found another bug in the mergepersonquery code when test started running; another nonetype issue. Couldn't get the subprocess stuff to work right, so changed code to just call Gramps with capture, similar to export_tests. This in turn required that importxml and exportxml be slightly changed to support StringIO redirection of stdin and stdout. And test_util needed a change to allow stdout to accept an unencoded stream, so I added an option to use BytesIO for this test as well. Added some code to save input, result, and expected data to files in Gramps temp directory for debugging whenever an error occurred. Easier to use my editor in diff mode than look at the outputs. --- .travis.yml | 4 +- gramps/gen/merge/mergefamilyquery.py | 32 +- gramps/gen/merge/mergepersonquery.py | 9 +- gramps/gen/merge/test/merge_ref_test.py | 2386 +++++++++++------------ gramps/plugins/export/exportxml.py | 5 +- gramps/plugins/importer/importxml.py | 5 +- gramps/test/test_util.py | 8 +- 7 files changed, 1222 insertions(+), 1227 deletions(-) diff --git a/.travis.yml b/.travis.yml index 402434903..7b28180ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,8 +89,8 @@ before_script: - export PYTHONPATH=meta # set module exclusions. --exclude=TestUser because of older version of mock # without configure_mock - - export EXCLUDE="--exclude=TestcaseGenerator --exclude=vcard - --exclude=merge_ref_test" + - export EXCLUDE="--exclude=TestcaseGenerator --exclude=vcard" +# --exclude=merge_ref_test" # set GRAMPS_RESOURCES for locale, data,image and documentation - export GRAMPS_RESOURCES=. diff --git a/gramps/gen/merge/mergefamilyquery.py b/gramps/gen/merge/mergefamilyquery.py index 1cf8dcce4..2ac2a8c16 100644 --- a/gramps/gen/merge/mergefamilyquery.py +++ b/gramps/gen/merge/mergefamilyquery.py @@ -134,15 +134,35 @@ class MergeFamilyQuery: with DbTxn(_('Merge Family'), self.database) as trans: - phoenix_father = self.database.get_person_from_handle(self.phoenix_fh) - titanic_father = self.database.get_person_from_handle(self.titanic_fh) - self.merge_person(phoenix_father, titanic_father, 'father', trans) + if self.phoenix_fh != self.titanic_fh: + if self.phoenix_fh: + phoenix_father = self.database.get_person_from_handle( + self.phoenix_fh) + else: + phoenix_father = None + if self.titanic_fh: + titanic_father = self.database.get_person_from_handle( + self.titanic_fh) + else: + titanic_father = None + self.merge_person(phoenix_father, titanic_father, + 'father', trans) - phoenix_mother = self.database.get_person_from_handle(self.phoenix_mh) - titanic_mother = self.database.get_person_from_handle(self.titanic_mh) + if self.phoenix_mh != self.titanic_mh: + if self.phoenix_mh: + phoenix_mother = self.database.get_person_from_handle( + self.phoenix_mh) + else: + phoenix_mother = None + if self.titanic_mh: + titanic_mother = self.database.get_person_from_handle( + self.titanic_mh) + else: + titanic_mother = None + self.merge_person(phoenix_mother, titanic_mother, + 'mother', trans) self.phoenix = self.database.get_family_from_handle(new_handle) self.titanic = self.database.get_family_from_handle(old_handle) - self.merge_person(phoenix_mother, titanic_mother, 'mother', trans) phoenix_father = self.database.get_person_from_handle(self.phoenix_fh) phoenix_mother = self.database.get_person_from_handle(self.phoenix_mh) diff --git a/gramps/gen/merge/mergepersonquery.py b/gramps/gen/merge/mergepersonquery.py index 138323a55..f93b89a2f 100644 --- a/gramps/gen/merge/mergepersonquery.py +++ b/gramps/gen/merge/mergepersonquery.py @@ -93,10 +93,11 @@ class MergePersonQuery: family_father_handle = family.get_father_handle() spouse_handle = family.get_mother_handle() if \ new_handle == family_father_handle else family_father_handle - spouse = self.database.get_person_from_handle(spouse_handle) - if spouse: - spouse.remove_family_handle(family_handle) - self.database.commit_person(spouse, trans) + if spouse_handle: + spouse = self.database.get_person_from_handle(spouse_handle) + if spouse: + spouse.remove_family_handle(family_handle) + self.database.commit_person(spouse, trans) # replace the family in lds ordinances for (dummy, person_handle) in self.database.find_backlink_handles( family_handle, ['Person']): diff --git a/gramps/gen/merge/test/merge_ref_test.py b/gramps/gen/merge/test/merge_ref_test.py index a5a1f6a22..c8e0520f4 100644 --- a/gramps/gen/merge/test/merge_ref_test.py +++ b/gramps/gen/merge/test/merge_ref_test.py @@ -25,60 +25,39 @@ objects than the objects merging. import unittest import time import os -import subprocess -import libxml2 -import libxslt +from io import BytesIO +import difflib +import copy +import lxml.etree as ET from gramps.plugins.lib.libgrampsxml import GRAMPS_XML_VERSION -from ...const import ROOT_DIR, USER_PLUGINS +from gramps.gen.const import DATA_DIR, USER_PLUGINS, TEMP_DIR from gramps.version import VERSION -from ...lib import Name, Surname -from ...const import GRAMPS_LOCALE as glocale +from gramps.gen.lib import Name, Surname +from gramps.test.test_util import Gramps +from gramps.cli.user import User +from gramps.gen.const import GRAMPS_LOCALE as glocale _ = glocale.translation.sgettext HAS_CLIMERGE = os.path.isdir(os.path.join(USER_PLUGINS, 'CliMerge')) HAS_EXPORTRAW = os.path.isdir(os.path.join(USER_PLUGINS, 'ExportRaw')) +NS_G = 'http://gramps-project.org/xml/%s/' % GRAMPS_XML_VERSION +NSP = "{%s}" % NS_G -class CopiedDoc: - """Context manager that creates a deep copy of an libxml-xml document.""" - def __init__(self, xmldoc): - self.xmldoc = xmldoc - self.copy = libxml2.readDoc(str(self.xmldoc), '', None, - libxml2.XML_PARSE_NONET) - - def __enter__(self): - return self.copy - - def __exit__(self, exc_type, exc_val, exc_tb): - self.copy.freeDoc() - return False - -class XpathContext: - """Context manager that creates a libxml2 xpath context that allows - evaluation of xpath expressions.""" - def __init__(self, xmldoc): - self.xmldoc = xmldoc - self.ctxt = self.xmldoc.xpathNewContext() - self.ctxt.xpathRegisterNs('g', 'http://gramps-project.org/xml/%s/' % - GRAMPS_XML_VERSION) - - def __enter__(self): - return self.ctxt - - def __exit__(self, exc_type, exc_val, exc_tb): - self.ctxt.xpathFreeContext() - return False @unittest.skipUnless(HAS_CLIMERGE and HAS_EXPORTRAW, - 'These tests need the 3rd-party plugins "CliMerge" and "ExportRaw".') + 'These tests need the 3rd-party plugins "CliMerge" ' + 'and "ExportRaw".') class BaseMergeCheck(unittest.TestCase): + """ Base class for the merge tests """ def base_setup(self): """Set up code needed by all tests.""" date = time.localtime(time.time()) - libxml2.keepBlanksDefault(0) - styledoc = libxml2.parseFile(os.path.join(ROOT_DIR, - "../data/gramps_canonicalize.xsl")) - self.style = libxslt.parseStylesheetDoc(styledoc) + # libxml2.keepBlanksDefault(0) + self.parser = ET.XMLParser(remove_blank_text=True) + styledoc = ET.parse(os.path.join(DATA_DIR, "gramps_canonicalize.xsl"), + parser=self.parser) + self.transform = ET.XSLT(styledoc) self.basedoc = None self.base_str = """ \n """ % (GRAMPS_XML_VERSION, GRAMPS_XML_VERSION, GRAMPS_XML_VERSION, - date[0], date[1], date[2], VERSION) - - def tearDown(self): - self.basedoc.freeDoc() + date[0], date[1], date[2], VERSION) def canonicalize(self, doctxt): """ @@ -103,90 +79,118 @@ class BaseMergeCheck(unittest.TestCase): :returns: The text but in canonical form. :rtype: string """ - result = '' - if isinstance(doctxt, basestring): - doc = libxml2.readDoc(doctxt, '', None, libxml2.XML_PARSE_NONET) - elif isinstance(doctxt, libxml2.xmlDoc): + if isinstance(doctxt, bytes): + doc = ET.fromstring(doctxt, parser=self.parser) + elif isinstance(doctxt, ET._Element): doc = doctxt else: raise TypeError - param = {} - canonical_doc = self.style.applyStylesheet(doc, param) - result = self.style.saveResultToString(canonical_doc) - canonical_doc.freeDoc() - if isinstance(doctxt, basestring): - doc.freeDoc() + canonical_doc = self.transform(doc) + result = ET.tostring(canonical_doc, pretty_print=True) + #print(str(result, 'utf-8')) return result def do_case(self, phoenix_id, titanic_id, input_doc, expect_doc, - test_error_str='', debug=False): + test_error_str=''): """Do the merge and "assert" the result.""" - process = subprocess.Popen('python Gramps.py -d .ImportXML ' - '--config=preferences.eprefix:DEFAULT ' - '-i - -f gramps -a tool ' - '-p "name=climerge,primary=%s,secondary=%s" ' - '-e - -f gramps' % (phoenix_id, titanic_id), - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, shell=True) - result_str, err_str = process.communicate(str(input_doc)) + gramps = Gramps(user=User(auto_accept=True, quiet=True)) + result_str, err_str = gramps.run( + '-d', '.ImportXML', '--config=preferences.eprefix:DEFAULT', + '-i', '-', '-f', 'gramps', '-a', 'tool', '-p', + "name=climerge,primary=%s,secondary=%s" % (phoenix_id, titanic_id), + '-e', '-', '-f', 'gramps', stdin=BytesIO(input_doc), bytesio=True) + self.check_results(input_doc, expect_doc, result_str, err_str, + test_error_str) + + def check_results(self, input_doc, expect_doc, result_str, err_str, + test_error_str=''): + input_file = os.path.join(TEMP_DIR, "merge_test_input.gramps") + try: + os.remove(input_file) + except OSError: + pass + if err_str: if test_error_str: self.assertIn(test_error_str, err_str) return else: if "Traceback (most recent call last):" in err_str: + inp = self.canonicalize(input_doc) + inpt = open(input_file, mode='wb') + inpt.write(inp) + inpt.close() raise Exception(err_str) - if debug: - print('input :', self.canonicalize(input_doc)) - print('result:', self.canonicalize(result_str)) - print('expect:', self.canonicalize(expect_doc)) - self.assertEqual(self.canonicalize(result_str), - self.canonicalize(expect_doc)) + result = self.canonicalize(result_str) + result_file = os.path.join(TEMP_DIR, "merge_test_result.gramps") + try: + os.remove(result_file) + except OSError: + pass + expect = self.canonicalize(expect_doc) + expect_file = os.path.join(TEMP_DIR, "merge_test_expected.gramps") + try: + os.remove(expect_file) + except OSError: + pass + if result != expect: + res = open(result_file, mode='wb') + res.write(result) + res.close() + eres = open(expect_file, mode='wb') + eres.write(expect) + eres.close() + inp = self.canonicalize(input_doc) + inpt = open(input_file, mode='wb') + inpt.write(inp) + inpt.close() + result = result.decode('utf-8') + expect = expect.decode('utf-8') + diff = difflib.ndiff(result, expect) + msg = "" + for line in diff: + msg += line + self.fail(msg) def do_family_case(self, phoenix_id, titanic_id, father_h, mother_h, - input_doc, expect_doc, test_error_str='', debug=False): - process = subprocess.Popen('python Gramps.py -d .ImportXML ' - '--config=preferences.eprefix:DEFAULT ' - '-i - -f gramps -a tool ' - '-p "name=climerge,primary=%s,secondary=%s,father_h=%s,mother_h=%s" ' - '-e - -f gramps' % (phoenix_id, titanic_id, father_h, mother_h), - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, shell=True) - result_str, err_str = process.communicate(str(input_doc)) - if err_str: - if test_error_str: - self.assertIn(test_error_str, err_str) - return - if debug: - print('input :', self.canonicalize(input_doc)) - print('result:', self.canonicalize(result_str)) - print('expect:', self.canonicalize(expect_doc)) - self.assertEqual(self.canonicalize(result_str), - self.canonicalize(expect_doc)) + input_doc, expect_doc, test_error_str=''): + gramps = Gramps(user=User(auto_accept=True, quiet=True)) + result_str, err_str = gramps.run( + '-d', '.ImportXML', '--config=preferences.eprefix:DEFAULT', + '-i', '-', '-f', 'gramps', '-a', 'tool', '-p', + "name=climerge,primary=%s,secondary=%s,father_h=%s,mother_h=%s" % + (phoenix_id, titanic_id, father_h, mother_h), + '-e', '-', '-f', 'gramps', stdin=BytesIO(input_doc), bytesio=True) + self.check_results(input_doc, expect_doc, result_str, err_str, + test_error_str) def raw_contains(self, phoenix_id, titanic_id, input_doc, expect_str, - test_error_str='', debug=False): - process = subprocess.Popen('python Gramps.py -d .ImportXML ' - '--config=preferences.eprefix:DEFAULT ' - '-i - -f gramps -a tool ' - '-p "name=climerge,primary=%s,secondary=%s" ' - '-e - -f raw' % (phoenix_id, titanic_id), - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, shell=True) - result_str, err_str = process.communicate(str(input_doc)) + test_error_str=''): + gramps = Gramps(user=User(auto_accept=True, quiet=True)) + result_str, err_str = gramps.run( + '-d', '.ImportXML', '--config=preferences.eprefix:DEFAULT', + '-i', '-', '-f', 'gramps', '-a', 'tool', '-p', + "name=climerge,primary=%s,secondary=%s" % (phoenix_id, titanic_id), + '-e', '-', '-f', 'raw', stdin=BytesIO(input_doc), bytesio=False) if err_str: if test_error_str: self.assertIn(test_error_str, err_str) return - if debug: - print('input :', self.canonicalize(input_doc)) - print('result:', result_str) - print('expect:', expect_str) - # should I order/canonicalise things? - self.assertIn(expect_str, result_str) + if expect_str not in result_str: + msg = '\n***** result:\n' + result_str + \ + '\n***** expect:\n' + expect_str + inp = self.canonicalize(input_doc) + input_file = os.path.join(TEMP_DIR, "merge_test_input.gramps") + try: + os.remove(input_file) + except OSError: + pass + inpt = open(input_file, mode='wb') + inpt.write(inp) + inpt.close() + self.fail(msg) + + #------------------------------------------------------------------------- # # PersonCheck class @@ -257,11 +261,13 @@ class PersonCheck(BaseMergeCheck): - + Place 0 + - + Place 1 + @@ -281,70 +287,68 @@ class PersonCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_event_merge(self): """Merge two events""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - eventref = ctxt.xpathEval( - "//g:person[@handle='_i0001']/g:eventref")[0] - eventref.setProp('hlink', '_e0000') - event = ctxt.xpathEval("//g:event[@handle='_e0001']")[0] - event.unlinkNode() - event.freeNode() - self.do_case('E0000', 'E0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + eventref = expect.xpath("//g:person[@handle='_i0001']/g:eventref", + namespaces={"g": NS_G})[0] + eventref.attrib['hlink'] = '_e0000' + event = expect.xpath("//g:event[@handle='_e0001']", + namespaces={"g": NS_G})[0] + event.getparent().remove(event) + self.do_case('E0000', 'E0001', self.basedoc, expect) + #print(str(ET.tostring(expect, pretty_print=True), 'utf-8')) def test_place_merge(self): """Merge two places""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - place = ctxt.xpathEval( - "//g:person[@handle='_i0001']/g:lds_ord/g:place")[0] - place.setProp('hlink', '_p0000') - placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0001']")[0] - placeobj.unlinkNode() - placeobj.freeNode() - placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0000']")[0] - placeobj.newChild(None, 'alt_name', 'Place 1') - self.do_case('P0000', 'P0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + place = expect.xpath("//g:person[@handle='_i0001']/g:lds_ord/g:place", + namespaces={"g": NS_G})[0] + place.attrib['hlink'] = '_p0000' + placeobj = expect.xpath("//g:placeobj[@handle='_p0001']", + namespaces={"g": NS_G})[0] + placeobj.getparent().remove(placeobj) + placeobj = expect.xpath("//g:placeobj[@handle='_p0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(placeobj, NSP + 'pname', value='Place 1') + self.do_case('P0000', 'P0001', self.basedoc, expect) def test_citation_merge(self): """Merge two citations""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - srcref = ctxt.xpathEval( - "//g:person[@handle='_i0001']/g:citationref")[0] - srcref.setProp('hlink', '_c0000') - citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0] - citation.unlinkNode() - citation.freeNode() - self.do_case('C0000', 'C0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + srcref = expect.xpath("//g:person[@handle='_i0001']/g:citationref", + namespaces={"g": NS_G})[0] + srcref.attrib['hlink'] = '_c0000' + citation = expect.xpath("//g:citation[@handle='_c0001']", + namespaces={"g": NS_G})[0] + citation.getparent().remove(citation) + self.do_case('C0000', 'C0001', self.basedoc, expect) def test_media_merge(self): """Merge two media objects""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - objref = ctxt.xpathEval( - "//g:person[@handle='_i0001']/g:objref")[0] - objref.setProp('hlink', '_o0000') - object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0] - object_.unlinkNode() - object_.freeNode() - self.do_case('O0000', 'O0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + objref = expect.xpath("//g:person[@handle='_i0001']/g:objref", + namespaces={"g": NS_G})[0] + objref.attrib['hlink'] = '_o0000' + object_ = expect.xpath("//g:object[@handle='_o0001']", + namespaces={"g": NS_G})[0] + object_.getparent().remove(object_) + self.do_case('O0000', 'O0001', self.basedoc, expect) def test_note_merge(self): """Merge two notes""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - noteref = ctxt.xpathEval( - "//g:person[@handle='_i0001']/g:noteref")[0] - noteref.setProp('hlink', '_n0000') - note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0] - note.unlinkNode() - note.freeNode() - self.do_case('N0000', 'N0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + noteref = expect.xpath("//g:person[@handle='_i0001']/g:noteref", + namespaces={"g": NS_G})[0] + noteref.attrib['hlink'] = '_n0000' + note = expect.xpath("//g:note[@handle='_n0001']", + namespaces={"g": NS_G})[0] + note.getparent().remove(note) + self.do_case('N0000', 'N0001', self.basedoc, expect) + #------------------------------------------------------------------------- # @@ -410,11 +414,13 @@ class FamilyCheck(BaseMergeCheck): - + Place 0 + - + Place 1 + @@ -434,70 +440,67 @@ class FamilyCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_event_merge(self): """Merge two events""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - eventref = ctxt.xpathEval( - "//g:family[@handle='_f0001']/g:eventref")[0] - eventref.setProp('hlink', '_e0000') - event = ctxt.xpathEval("//g:event[@handle='_e0001']")[0] - event.unlinkNode() - event.freeNode() - self.do_case('E0000', 'E0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + eventref = expect.xpath("//g:family[@handle='_f0001']/g:eventref", + namespaces={"g": NS_G})[0] + eventref.attrib['hlink'] = '_e0000' + event = expect.xpath("//g:event[@handle='_e0001']", + namespaces={"g": NS_G})[0] + event.getparent().remove(event) + self.do_case('E0000', 'E0001', self.basedoc, expect) def test_place_merge(self): """Merge two places""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - place = ctxt.xpathEval( - "//g:family[@handle='_f0001']/g:lds_ord/g:place")[0] - place.setProp('hlink', '_p0000') - placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0001']")[0] - placeobj.unlinkNode() - placeobj.freeNode() - placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0000']")[0] - placeobj.newChild(None, 'alt_name', 'Place 1') - self.do_case('P0000', 'P0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + place = expect.xpath("//g:family[@handle='_f0001']/g:lds_ord/g:place", + namespaces={"g": NS_G})[0] + place.attrib['hlink'] = '_p0000' + placeobj = expect.xpath("//g:placeobj[@handle='_p0001']", + namespaces={"g": NS_G})[0] + placeobj.getparent().remove(placeobj) + placeobj = expect.xpath("//g:placeobj[@handle='_p0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(placeobj, NSP + 'pname', value='Place 1') + self.do_case('P0000', 'P0001', self.basedoc, expect) def test_citation_merge(self): """Merge two citations""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - citref = ctxt.xpathEval( - "//g:family[@handle='_f0001']/g:citationref")[0] - citref.setProp('hlink', '_c0000') - citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0] - citation.unlinkNode() - citation.freeNode() - self.do_case('C0000', 'C0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + citref = expect.xpath("//g:family[@handle='_f0001']/g:citationref", + namespaces={"g": NS_G})[0] + citref.attrib['hlink'] = '_c0000' + citation = expect.xpath("//g:citation[@handle='_c0001']", + namespaces={"g": NS_G})[0] + citation.getparent().remove(citation) + self.do_case('C0000', 'C0001', self.basedoc, expect) def test_media_merge(self): """Merge two media objects""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - objref = ctxt.xpathEval( - "//g:family[@handle='_f0001']/g:objref")[0] - objref.setProp('hlink', '_o0000') - object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0] - object_.unlinkNode() - object_.freeNode() - self.do_case('O0000', 'O0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + objref = expect.xpath("//g:family[@handle='_f0001']/g:objref", + namespaces={"g": NS_G})[0] + objref.attrib['hlink'] = '_o0000' + object_ = expect.xpath("//g:object[@handle='_o0001']", + namespaces={"g": NS_G})[0] + object_.getparent().remove(object_) + self.do_case('O0000', 'O0001', self.basedoc, expect) def test_note_merge(self): """Merge two notes""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - noteref = ctxt.xpathEval( - "//g:family[@handle='_f0001']/g:noteref")[0] - noteref.setProp('hlink', '_n0000') - note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0] - note.unlinkNode() - note.freeNode() - self.do_case('N0000', 'N0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + noteref = expect.xpath("//g:family[@handle='_f0001']/g:noteref", + namespaces={"g": NS_G})[0] + noteref.attrib['hlink'] = '_n0000' + note = expect.xpath("//g:note[@handle='_n0001']", + namespaces={"g": NS_G})[0] + note.getparent().remove(note) + self.do_case('N0000', 'N0001', self.basedoc, expect) + #------------------------------------------------------------------------- # @@ -549,11 +552,13 @@ class EventCheck(BaseMergeCheck): - + Place 0 + - + Place 1 + @@ -573,58 +578,56 @@ class EventCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_place_merge(self): """Merge two places""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - place = ctxt.xpathEval( - "//g:event[@handle='_e0001']/g:place")[0] - place.setProp('hlink', '_p0000') - placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0001']")[0] - placeobj.unlinkNode() - placeobj.freeNode() - placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0000']")[0] - placeobj.newChild(None, 'alt_name', 'Place 1') - self.do_case('P0000', 'P0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + place = expect.xpath("//g:event[@handle='_e0001']/g:place", + namespaces={"g": NS_G})[0] + place.attrib['hlink'] = '_p0000' + placeobj = expect.xpath("//g:placeobj[@handle='_p0001']", + namespaces={"g": NS_G})[0] + placeobj.getparent().remove(placeobj) + placeobj = expect.xpath("//g:placeobj[@handle='_p0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(placeobj, NSP + 'pname', value='Place 1') + self.do_case('P0000', 'P0001', self.basedoc, expect) def test_citation_merge(self): """Merge two citations""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - citref = ctxt.xpathEval( - "//g:event[@handle='_e0001']/g:citationref")[0] - citref.setProp('hlink', '_c0000') - citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0] - citation.unlinkNode() - citation.freeNode() - self.do_case('C0000', 'C0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + citref = expect.xpath("//g:event[@handle='_e0001']/g:citationref", + namespaces={"g": NS_G})[0] + citref.attrib['hlink'] = '_c0000' + citation = expect.xpath("//g:citation[@handle='_c0001']", + namespaces={"g": NS_G})[0] + citation.getparent().remove(citation) + self.do_case('C0000', 'C0001', self.basedoc, expect) def test_media_merge(self): """Merge two media objects""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - objref = ctxt.xpathEval( - "//g:event[@handle='_e0001']/g:objref")[0] - objref.setProp('hlink', '_o0000') - object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0] - object_.unlinkNode() - object_.freeNode() - self.do_case('O0000', 'O0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + objref = expect.xpath("//g:event[@handle='_e0001']/g:objref", + namespaces={"g": NS_G})[0] + objref.attrib['hlink'] = '_o0000' + object_ = expect.xpath("//g:object[@handle='_o0001']", + namespaces={"g": NS_G})[0] + object_.getparent().remove(object_) + self.do_case('O0000', 'O0001', self.basedoc, expect) def test_note_merge(self): """Merge two notes""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - noteref = ctxt.xpathEval( - "//g:event[@handle='_e0001']/g:noteref")[0] - noteref.setProp('hlink', '_n0000') - note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0] - note.unlinkNode() - note.freeNode() - self.do_case('N0000', 'N0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + noteref = expect.xpath("//g:event[@handle='_e0001']/g:noteref", + namespaces={"g": NS_G})[0] + noteref.attrib['hlink'] = '_n0000' + note = expect.xpath("//g:note[@handle='_n0001']", + namespaces={"g": NS_G})[0] + note.getparent().remove(note) + self.do_case('N0000', 'N0001', self.basedoc, expect) + #------------------------------------------------------------------------- # @@ -658,14 +661,16 @@ class PlaceCheck(BaseMergeCheck): - + Place 0 + - + Place 1 + @@ -688,44 +693,42 @@ class PlaceCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_citation_merge(self): """Merge two citations""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - citref = ctxt.xpathEval( - "//g:placeobj[@handle='_p0001']/g:citationref")[0] - citref.setProp('hlink', '_c0000') - citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0] - citation.unlinkNode() - citation.freeNode() - self.do_case('C0000', 'C0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + citref = expect.xpath("//g:placeobj[@handle='_p0001']/g:citationref", + namespaces={"g": NS_G})[0] + citref.attrib['hlink'] = '_c0000' + citation = expect.xpath("//g:citation[@handle='_c0001']", + namespaces={"g": NS_G})[0] + citation.getparent().remove(citation) + self.do_case('C0000', 'C0001', self.basedoc, expect) def test_media_merge(self): """Merge two media objects""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - objref = ctxt.xpathEval( - "//g:placeobj[@handle='_p0001']/g:objref")[0] - objref.setProp('hlink', '_o0000') - object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0] - object_.unlinkNode() - object_.freeNode() - self.do_case('O0000', 'O0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + objref = expect.xpath("//g:placeobj[@handle='_p0001']/g:objref", + namespaces={"g": NS_G})[0] + objref.attrib['hlink'] = '_o0000' + object_ = expect.xpath("//g:object[@handle='_o0001']", + namespaces={"g": NS_G})[0] + object_.getparent().remove(object_) + self.do_case('O0000', 'O0001', self.basedoc, expect) def test_note_merge(self): """Merge two notes""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - noteref = ctxt.xpathEval( - "//g:placeobj[@handle='_p0001']/g:noteref")[0] - noteref.setProp('hlink', '_n0000') - note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0] - note.unlinkNode() - note.freeNode() - self.do_case('N0000', 'N0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + noteref = expect.xpath("//g:placeobj[@handle='_p0001']/g:noteref", + namespaces={"g": NS_G})[0] + noteref.attrib['hlink'] = '_n0000' + note = expect.xpath("//g:note[@handle='_n0001']", + namespaces={"g": NS_G})[0] + note.getparent().remove(note) + self.do_case('N0000', 'N0001', self.basedoc, expect) + #------------------------------------------------------------------------- # @@ -791,49 +794,47 @@ class SourceCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) #def test_citation_merge(self): SEE special cases. def test_repo_merge(self): """Merge two repositories""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - # Adjust the repository reference in expectation. - reporef = ctxt.xpathEval( - "//g:source[@handle='_s0001']/g:reporef")[0] - reporef.setProp('hlink', '_r0000') - # Remove one repository in expectation. - repo = ctxt.xpathEval("//g:repository[@handle='_r0001']")[0] - repo.unlinkNode() - repo.freeNode() - # Do the actual merger and comparison. - self.do_case('R0000', 'R0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + # Adjust the repository reference in expectation. + reporef = expect.xpath("//g:source[@handle='_s0001']/g:reporef", + namespaces={"g": NS_G})[0] + reporef.attrib['hlink'] = '_r0000' + # Remove one repository in expectation. + repo = expect.xpath("//g:repository[@handle='_r0001']", + namespaces={"g": NS_G})[0] + repo.getparent().remove(repo) + # Do the actual merger and comparison. + self.do_case('R0000', 'R0001', self.basedoc, expect) def test_media_merge(self): """Merge two media objects""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - objref = ctxt.xpathEval( - "//g:source[@handle='_s0001']/g:objref")[0] - objref.setProp('hlink', '_o0000') - object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0] - object_.unlinkNode() - object_.freeNode() - self.do_case('O0000', 'O0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + objref = expect.xpath("//g:source[@handle='_s0001']/g:objref", + namespaces={"g": NS_G})[0] + objref.attrib['hlink'] = '_o0000' + object_ = expect.xpath("//g:object[@handle='_o0001']", + namespaces={"g": NS_G})[0] + object_.getparent().remove(object_) + self.do_case('O0000', 'O0001', self.basedoc, expect) def test_note_merge(self): """Merge two notes""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - noteref = ctxt.xpathEval( - "//g:source[@handle='_s0001']/g:noteref")[0] - noteref.setProp('hlink', '_n0000') - note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0] - note.unlinkNode() - note.freeNode() - self.do_case('N0000', 'N0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + noteref = expect.xpath("//g:source[@handle='_s0001']/g:noteref", + namespaces={"g": NS_G})[0] + noteref.attrib['hlink'] = '_n0000' + note = expect.xpath("//g:note[@handle='_n0001']", + namespaces={"g": NS_G})[0] + note.getparent().remove(note) + self.do_case('N0000', 'N0001', self.basedoc, expect) + #------------------------------------------------------------------------- # @@ -895,32 +896,32 @@ class RepoCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_citation_merge(self): """Merge two citations""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - citref = ctxt.xpathEval( - "//g:repository[@handle='_r0001']/g:address/g:citationref")[0] - citref.setProp('hlink', '_c0000') - citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0] - citation.unlinkNode() - citation.freeNode() - self.do_case('C0000', 'C0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + citref = expect.xpath("//g:repository[@handle='_r0001']" + "/g:address/g:citationref", + namespaces={"g": NS_G})[0] + citref.attrib['hlink'] = '_c0000' + citation = expect.xpath("//g:citation[@handle='_c0001']", + namespaces={"g": NS_G})[0] + citation.getparent().remove(citation) + self.do_case('C0000', 'C0001', self.basedoc, expect) def test_note_merge(self): """Merge two notes""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - noteref = ctxt.xpathEval( - "//g:repository[@handle='_r0001']/g:noteref")[0] - noteref.setProp('hlink', '_n0000') - note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0] - note.unlinkNode() - note.freeNode() - self.do_case('N0000', 'N0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + noteref = expect.xpath("//g:repository[@handle='_r0001']/g:noteref", + namespaces={"g": NS_G})[0] + noteref.attrib['hlink'] = '_n0000' + note = expect.xpath("//g:note[@handle='_n0001']", + namespaces={"g": NS_G})[0] + note.getparent().remove(note) + self.do_case('N0000', 'N0001', self.basedoc, expect) + #------------------------------------------------------------------------- # @@ -974,32 +975,30 @@ class MediaCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_citation_merge(self): """Merge two citations""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - citref = ctxt.xpathEval( - "//g:object[@handle='_o0001']/g:citationref")[0] - citref.setProp('hlink', '_c0000') - citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0] - citation.unlinkNode() - citation.freeNode() - self.do_case('C0000', 'C0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + citref = expect.xpath("//g:object[@handle='_o0001']/g:citationref", + namespaces={"g": NS_G})[0] + citref.attrib['hlink'] = '_c0000' + citation = expect.xpath("//g:citation[@handle='_c0001']", + namespaces={"g": NS_G})[0] + citation.getparent().remove(citation) + self.do_case('C0000', 'C0001', self.basedoc, expect) def test_note_merge(self): """Merge two notes""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - noteref = ctxt.xpathEval( - "//g:object[@handle='_o0001']/g:noteref")[0] - noteref.setProp('hlink', '_n0000') - note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0] - note.unlinkNode() - note.freeNode() - self.do_case('N0000', 'N0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + noteref = expect.xpath("//g:object[@handle='_o0001']/g:noteref", + namespaces={"g": NS_G})[0] + noteref.attrib['hlink'] = '_n0000' + note = expect.xpath("//g:note[@handle='_n0001']", + namespaces={"g": NS_G})[0] + note.getparent().remove(note) + self.do_case('N0000', 'N0001', self.basedoc, expect) #========================================================================= # @@ -1007,6 +1006,7 @@ class MediaCheck(BaseMergeCheck): # #========================================================================= + #------------------------------------------------------------------------- # # SourceSourceCheck class @@ -1061,49 +1061,53 @@ class SourceSourceCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_citation_merge(self): - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - citrefs = ctxt.xpathEval( - "//g:citation[@handle='_c0001']/g:objref/g:citationref") - citrefs[0].setProp('hlink', '_c0002') - citations = ctxt.xpathEval("//g:citation[@handle='_c0003']") - citations[0].unlinkNode() - citations[0].freeNode() - self.do_case('C0002', 'C0003', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + citrefs = expect.xpath("//g:citation[@handle='_c0001']" + "/g:objref/g:citationref", + namespaces={"g": NS_G}) + citrefs[0].attrib['hlink'] = '_c0002' + citations = expect.xpath("//g:citation[@handle='_c0003']", + namespaces={"g": NS_G}) + citations[0].getparent().remove(citations[0]) + self.do_case('C0002', 'C0003', self.basedoc, expect) def test_citation_cross_merge(self): - with XpathContext(self.basedoc) as input_ctxt: - input_citrefs = input_ctxt.xpathEval( - "//g:citation/g:objref/g:citationref") - input_citrefs[0].setProp('hlink', '_c0001') - input_citrefs[1].setProp('hlink', '_c0000') - rmcit = input_ctxt.xpathEval("//g:citation[@handle='_c0002']") - rmcit[0].unlinkNode() - rmcit[0].freeNode() - rmcit = input_ctxt.xpathEval("//g:citation[@handle='_c0003']") - rmcit[0].unlinkNode() - rmcit[0].freeNode() - rmsrc = input_ctxt.xpathEval("//g:source[@handle='_s0001']") - rmsrc[0].unlinkNode() - rmsrc[0].freeNode() - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - citrefs = ctxt.xpathEval( - "//g:citation[@handle='_c0000']/g:objref/g:citationref") - citrefs[0].setProp('hlink', '_c0000') - # add objref - objref = ctxt.xpathEval("//g:citation[@handle='_c0000']/g:objref") - objref2 = ctxt.xpathEval("//g:citation[@handle='_c0001']/g:objref") - objref[0].addNextSibling(objref2[0]) - # remove citation - citations = ctxt.xpathEval("//g:citation[@handle='_c0001']") - citations[0].unlinkNode() - citations[0].freeNode() - self.do_case('C0000', 'C0001', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + input_citrefs = input_ctxt.xpath("//g:citation/g:objref/g:citationref", + namespaces={"g": NS_G}) + input_citrefs[0].attrib['hlink'] = '_c0001' + input_citrefs[1].attrib['hlink'] = '_c0000' + rmcit = input_ctxt.xpath("//g:citation[@handle='_c0002']", + namespaces={"g": NS_G}) + rmcit[0].getparent().remove(rmcit[0]) + rmcit = input_ctxt.xpath("//g:citation[@handle='_c0003']", + namespaces={"g": NS_G}) + rmcit[0].getparent().remove(rmcit[0]) + rmsrc = input_ctxt.xpath("//g:source[@handle='_s0001']", + namespaces={"g": NS_G}) + rmsrc[0].getparent().remove(rmsrc[0]) + expect = copy.deepcopy(input_ctxt) + citrefs = expect.xpath("//g:citation[@handle='_c0000']/g:objref" + "/g:citationref", namespaces={"g": NS_G}) + citrefs[0].attrib['hlink'] = '_c0000' + # add objref + objref = expect.xpath("//g:citation[@handle='_c0000']/g:objref", + namespaces={"g": NS_G}) + objref2 = expect.xpath("//g:citation[@handle='_c0001']/g:objref", + namespaces={"g": NS_G}) + #objref[0].addNextSibling(objref2[0]) + objref[0].addnext(objref2[0]) + # remove citation + citations = expect.xpath("//g:citation[@handle='_c0001']", + namespaces={"g": NS_G}) + citations[0].getparent().remove(citations[0]) + input_doc = ET.tostring(input_ctxt) + self.do_case('C0000', 'C0001', input_doc, expect) + #------------------------------------------------------------------------- # @@ -1140,8 +1144,8 @@ class BirthCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) surname = Surname() surname.set_surname("Person 0") surname.set_prefix("") @@ -1153,24 +1157,27 @@ class BirthCheck(BaseMergeCheck): def test_birth_loss(self): # check that birth_ref_index is -1 + # input_doc = ET.fromstring(self.basedoc, parser=self.parser) expect_str = self.expect_str + "1, -1" self.raw_contains('E0000', 'E0001', self.basedoc, expect_str) def test_second_birth(self): # check that birth _ref_index is 2 - with XpathContext(self.basedoc) as ctxt: - events = ctxt.xpathEval("//g:events") - second_birth = events[0].newChild(None, 'event', None) - second_birth.newProp('handle', '_e0003') - second_birth.newProp('id', 'E0003') - second_birth.newChild(None, 'type', 'Birth') - second_birth.newChild(None, 'description', 'Event 3') - person = ctxt.xpathEval("//g:person[@handle='_i0000']") - eventref = person[0].newChild(None, 'eventref', None) - eventref.newProp('hlink', '_e0003') - eventref.newProp('role', 'Primary') - expect_str = self.expect_str + "1, 2" - self.raw_contains('E0000', 'E0001', self.basedoc, expect_str) + input_doc = ET.fromstring(self.basedoc, parser=self.parser) + events = input_doc.xpath("//g:events", + namespaces={"g": NS_G})[0] + second_birth = ET.SubElement(events, NSP + 'event', + handle='_e0003', id='E0003') + birthtype = ET.SubElement(second_birth, NSP + 'type') + birthtype.text = 'Birth' + birthdesc = ET.SubElement(second_birth, NSP + 'description') + birthdesc.text = 'Event 3' + person = input_doc.xpath("//g:person[@handle='_i0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(person, NSP + 'eventref', hlink='_e0003', role='Primary') + expect_str = self.expect_str + "1, 2" + input_str = ET.tostring(input_doc) + self.raw_contains('E0000', 'E0001', input_str, expect_str) def test_death_merge(self): # check that death_ref_index is -1 @@ -1179,19 +1186,22 @@ class BirthCheck(BaseMergeCheck): def test_second_death(self): # check that death _ref_index is 2 - with XpathContext(self.basedoc) as ctxt: - events = ctxt.xpathEval("//g:events") - second_death = events[0].newChild(None, 'event', None) - second_death.newProp('handle', '_e0003') - second_death.newProp('id', 'E0003') - second_death.newChild(None, 'type', 'Death') - second_death.newChild(None, 'description', 'Event 3') - person = ctxt.xpathEval("//g:person[@handle='_i0000']") - eventref = person[0].newChild(None, 'eventref', None) - eventref.newProp('hlink', '_e0003') - eventref.newProp('role', 'Primary') - expect_str = self.expect_str + "2, 1" - self.raw_contains('E0000', 'E0002', self.basedoc, expect_str) + input_doc = ET.fromstring(self.basedoc, parser=self.parser) + events = input_doc.xpath("//g:events", + namespaces={"g": NS_G})[0] + second_death = ET.SubElement(events, NSP + 'event', + handle='_e0003', id='E0003') + deathtype = ET.SubElement(second_death, NSP + 'type') + deathtype.text = 'Death' + deathdesc = ET.SubElement(second_death, NSP + 'description') + deathdesc.text = 'Event 3' + person = input_doc.xpath("//g:person[@handle='_i0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(person, NSP + 'eventref', hlink='_e0003', role='Primary') + expect_str = self.expect_str + "2, 1" + input_str = ET.tostring(input_doc) + self.raw_contains('E0000', 'E0002', input_str, expect_str) + #------------------------------------------------------------------------- # @@ -1217,96 +1227,91 @@ class PersonPersonCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_person_merge(self): """There is a person not involved in merger that references Titanic.""" - with XpathContext(self.basedoc) as input_ctxt: - people = input_ctxt.xpathEval("//g:people")[0] - person = people.newChild(None, 'person', None) - person.newProp('handle', '_i0002') - person.newProp('id', 'I0002') - person.newChild(None, 'gender', 'M') - name = person.newChild(None, 'name', None) - name.newProp('type', 'Birth Name') - name.newChild(None, 'surname', 'Person 2') - personref = person.newChild(None, 'personref', None) - personref.newProp('hlink', '_i0001') - personref.newProp('rel', 'Neighbour') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - personref = ctxt.xpathEval("//g:personref")[0] - personref.setProp('hlink', '_i0000') - person = ctxt.xpathEval("//g:person[@handle='_i0000']")[0] - altname = person.newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 1') - attr = person.newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0001') - person = ctxt.xpathEval("//g:person[@handle='_i0001']")[0] - person.unlinkNode() - person.freeNode() - self.do_case('I0000', 'I0001', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + people = input_ctxt.xpath("//g:people", + namespaces={"g": NS_G})[0] + person = ET.SubElement(people, NSP + 'person', + handle='_i0002', id='I0002') + ET.SubElement(person, NSP + 'gender').text = 'M' + name = ET.SubElement(person, NSP + 'name', type='Birth Name') + ET.SubElement(name, NSP + 'surname').text = 'Person 2' + ET.SubElement(person, NSP + 'personref', + hlink='_i0001', rel='Neighbour') + expect = copy.deepcopy(input_ctxt) + personref = expect.xpath("//g:personref", + namespaces={"g": NS_G})[0] + personref.attrib['hlink'] = '_i0000' + person = expect.xpath("//g:person[@handle='_i0000']", + namespaces={"g": NS_G})[0] + altname = ET.SubElement(person, NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 1' + ET.SubElement(person, NSP + 'attribute', + type='Merged Gramps ID', value='I0001') + person = expect.xpath("//g:person[@handle='_i0001']", + namespaces={"g": NS_G})[0] + person.getparent().remove(person) + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0001', input_doc, expect) def test_person_cross(self): """Phoenix has ref to Titanic and vice versa""" - with XpathContext(self.basedoc) as input_ctxt: - persons = input_ctxt.xpathEval("//g:person") - personref = persons[0].newChild(None, 'personref', None) - personref.newProp('hlink', '_i0001') - personref.newProp('rel','Neighbour East') - personref = persons[1].newChild(None, 'personref', None) - personref.newProp('hlink', '_i0000') - personref.newProp('rel','Neighbour West') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + ET.SubElement(persons[0], NSP + 'personref', + hlink='_i0001', rel='Neighbour East') + ET.SubElement(persons[1], NSP + 'personref', + hlink='_i0000', rel='Neighbour West') - personref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:personref")[0] - personref.setProp('hlink', '_i0000') - person = ctxt.xpathEval("//g:person[@handle='_i0000']")[0] - altname = person.newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 1') - attr = person.newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0001') - attr.addNextSibling(personref) # restore order of elements - personref = person.newChild(None, 'personref', None) - personref.newProp('hlink', '_i0000') - personref.newProp('rel', 'Neighbour West') - person = ctxt.xpathEval("//g:person[@handle='_i0001']")[0] - person.unlinkNode() - person.freeNode() - self.do_case('I0000', 'I0001', self.basedoc, expect) + expect = copy.deepcopy(input_ctxt) + personref = expect.xpath("//g:person[@handle='_i0000']/g:personref", + namespaces={"g": NS_G})[0] + personref.attrib['hlink'] = '_i0000' + person = expect.xpath("//g:person[@handle='_i0000']", + namespaces={"g": NS_G})[0] + altname = ET.SubElement(person, NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 1' + ET.SubElement(person, NSP + 'attribute', + type='Merged Gramps ID', value='I0001') + person.append(personref) # restore order of elements + personref = ET.SubElement(person, NSP + 'personref', + hlink='_i0000', rel='Neighbour West') + person = expect.xpath("//g:person[@handle='_i0001']", + namespaces={"g": NS_G})[0] + person.getparent().remove(person) + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0001', input_doc, expect) def test_person_self(self): """Titanic references itself""" - with XpathContext(self.basedoc) as input_ctxt: - person = input_ctxt.xpathEval("//g:person[@handle='_i0001']")[0] - personref = person.newChild(None, 'personref', None) - personref.newProp('hlink', '_i0001') - personref.newProp('rel', 'Neighbour') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - person = ctxt.xpathEval("//g:person[@handle='_i0000']")[0] - altname = person.newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 1') - attr = person.newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0001') - personref = person.newChild(None, 'personref', None) - personref.newProp('hlink', '_i0000') - personref.newProp('rel', 'Neighbour') - person = ctxt.xpathEval("//g:person[@handle='_i0001']")[0] - person.unlinkNode() - person.freeNode() - self.do_case('I0000', 'I0001', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + person = input_ctxt.xpath("//g:person[@handle='_i0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(person, NSP + 'personref', + hlink='_i0001', rel='Neighbour') + expect = copy.deepcopy(input_ctxt) + person = expect.xpath("//g:person[@handle='_i0000']", + namespaces={"g": NS_G})[0] + altname = ET.SubElement(person, NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 1' + ET.SubElement(person, NSP + 'attribute', + type='Merged Gramps ID', value='I0001') + ET.SubElement(person, NSP + 'personref', + hlink='_i0000', rel='Neighbour') + person = expect.xpath("//g:person[@handle='_i0001']", + namespaces={"g": NS_G})[0] + person.getparent().remove(person) + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0001', input_doc, expect) + #------------------------------------------------------------------------- # @@ -1344,31 +1349,30 @@ class ParentFamilyPersonCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_person_merge(self): """Merge two persons that are children in some family""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - childref = ctxt.xpathEval("//g:family[@handle='_f0001']/g:childref")[0] - childref.setProp('hlink', '_i0000') + expect = ET.fromstring(self.basedoc, parser=self.parser) + childref = expect.xpath("//g:family[@handle='_f0001']/g:childref", + namespaces={"g": NS_G})[0] + childref.attrib['hlink'] = '_i0000' + + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 1' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0001') + childof = expect.xpath("//g:person[@handle='_i0000']/g:childof", + namespaces={"g": NS_G})[0] + attr.addnext(childof) # restore order of elements + childof = ET.SubElement(persons[0], NSP + 'childof', hlink='_f0001') + persons[1].getparent().remove(persons[1]) + self.do_case('I0000', 'I0001', self.basedoc, expect) - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 1') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0001') - childof = ctxt.xpathEval("//g:person[@handle='_i0000']/g:childof")[0] - attr.addNextSibling(childof) # restore order of elements - childof = persons[0].newChild(None, 'childof', None) - childof.newProp('hlink', '_f0001') - persons[1].unlinkNode() - persons[1].freeNode() - self.do_case('I0000', 'I0001', self.basedoc, expect) #------------------------------------------------------------------------- # @@ -1419,411 +1423,385 @@ class FamilyPersonCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_titanic_no_fam(self): "Test merge of two persons where titanic is not a parent in a family" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - self.do_case('I0000', 'I0002', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + self.do_case('I0000', 'I0002', self.basedoc, expect) def test_no_fam_merge(self): """Test merge of two persons, both parents in a family, but such that the families will not merge.""" - with XpathContext(self.basedoc) as input_ctxt: - person = input_ctxt.xpathEval("//g:person[@handle='_i0002']")[0] - parentin = person.newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - father = family.newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - parentin = persons[0].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - father = ctxt.xpathEval("//g:family[@handle='_f0001']/g:father")[0] - father.setProp('hlink', '_i0000') - persons[2].unlinkNode() - persons[2].freeNode() - self.do_case('I0000', 'I0002', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + person = input_ctxt.xpath("//g:person[@handle='_i0002']", + namespaces={"g": NS_G})[0] + ET.SubElement(person, NSP + 'parentin', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'father', hlink='_i0002') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + ET.SubElement(persons[0], NSP + 'parentin', hlink='_f0001') + father = expect.xpath("//g:family[@handle='_f0001']/g:father", + namespaces={"g": NS_G})[0] + father.attrib['hlink'] = '_i0000' + persons[2].getparent().remove(persons[2]) + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect) def test_multi_rel(self): """Merge two persons where titanic has multiple family relationships with his partner, this should raise an error.""" - with XpathContext(self.basedoc) as input_ctxt: - persons = input_ctxt.xpathEval("//g:person") - parentin = persons[2].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - parentin = persons[3].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - parentin = persons[2].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0002') - parentin = persons[3].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0002') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - father = family.newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - mother = family.newChild(None, 'mother', None) - mother.newProp('hlink', '_i0003') - families = input_ctxt.xpathEval("//g:families")[0] - family = families.newChild(None, 'family', None) - family.newProp('handle', '_f0002') - family.newProp('id', 'F0002') - rel = family.newChild(None, 'rel', None) - rel.newProp('type', 'Married') - father = family.newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - mother = family.newChild(None, 'mother', None) - mother.newProp('hlink', '_i0003') - with CopiedDoc(self.basedoc) as expect: - self.do_case('I0000', 'I0002', self.basedoc, expect, - test_error_str=_("A person with multiple relations " - "with the same spouse is about to be merged. This is " - "beyond the capabilities of the merge routine. The " - "merge is aborted.")) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001') + ET.SubElement(persons[3], NSP + 'parentin', hlink='_f0001') + ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0002') + ET.SubElement(persons[3], NSP + 'parentin', hlink='_f0002') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'father', hlink='_i0002') + ET.SubElement(family, NSP + 'mother', hlink='_i0003') + families = input_ctxt.xpath("//g:families", + namespaces={"g": NS_G})[0] + family = ET.SubElement(families, NSP + 'family', + handle='_f0002', id='F0002') + ET.SubElement(family, NSP + 'rel', type='Married') + ET.SubElement(family, NSP + 'father', hlink='_i0002') + ET.SubElement(family, NSP + 'mother', hlink='_i0003') + input_doc = expect = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect, + test_error_str="A person with multiple relations " + "with the same spouse is about to be merged. This is " + "beyond the capabilities of the merge routine. The " + "merge is aborted.") def test_merge_fam(self): """Merge two persons such that also the families in which they are parents get merged.""" - with XpathContext(self.basedoc) as input_ctxt: - persons = input_ctxt.xpathEval("//g:person") - parentin = persons[1].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - parentin = persons[2].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - father = family.newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - mother = family.newChild(None, 'mother', None) - mother.newProp('hlink', '_i0001') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - parentin = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[1] - parentin.unlinkNode() - parentin.freeNode() - self.do_case('I0000', 'I0002', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + ET.SubElement(persons[1], NSP + 'parentin', hlink='_f0001') + ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'father', hlink='_i0002') + ET.SubElement(family, NSP + 'mother', hlink='_i0001') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + parentin = expect.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[1] + parentin.getparent().remove(parentin) + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect) def test_fam_none_merge(self): """Merge two persons, both father in families without mothers.""" - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0] - mother.unlinkNode() - mother.freeNode() - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - father = family.newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - person = input_ctxt.xpathEval("//g:person[@handle='_i0002']")[0] - parentin = person.newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - self.do_case('I0000', 'I0002', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother", + namespaces={"g": NS_G})[0] + mother.getparent().remove(mother) + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'father', hlink='_i0002') + person = input_ctxt.xpath("//g:person[@handle='_i0002']", + namespaces={"g": NS_G})[0] + parentin = ET.SubElement(person, NSP + 'parentin', hlink='_f0001', ) + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect) # Can't think of a testcase that would merge multiple families. def test_fam_mother_merge(self): """Merge two persons that are mothers in their respective families.""" - with XpathContext(self.basedoc) as input_ctxt: - persons = input_ctxt.xpathEval("//g:person") - parentin = persons[0].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - parentin = persons[3].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - father = family.newChild(None, 'father', None) - father.newProp('hlink', '_i0000') - mother = family.newChild(None, 'mother', None) - mother.newProp('hlink', '_i0003') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[1].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 3') - attr = persons[1].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0003') - parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[3].unlinkNode() - persons[3].freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - parentin = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[1] - parentin.unlinkNode() - parentin.freeNode() - self.do_case('I0001', 'I0003', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + ET.SubElement(persons[0], NSP + 'parentin', hlink='_f0001') + ET.SubElement(persons[3], NSP + 'parentin', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'father', hlink='_i0000') + ET.SubElement(family, NSP + 'mother', hlink='_i0003') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[1], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 3' + attr = ET.SubElement(persons[1], NSP + 'attribute', + type='Merged Gramps ID', value='I0003') + parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[3].getparent().remove(persons[3]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + parentin = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[1] + parentin.getparent().remove(parentin) + input_doc = ET.tostring(input_ctxt) + self.do_case('I0001', 'I0003', input_doc, expect) def test_childref_notyet(self): - """Merge two people leading to merger of families that have children.""" - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0] - mother.unlinkNode() - mother.freeNode() - persons = input_ctxt.xpathEval("//g:person") - childof = persons[1].newChild(None, 'childof', None) - childof.newProp('hlink', '_f0000') - families = input_ctxt.xpathEval("//g:family") - childref = families[0].newChild(None, 'childref', None) - childref.newProp('hlink', '_i0001') - parentin = persons[2].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - father = families[1].newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - childof = persons[3].newChild(None, 'childof', None) - childof.newProp('hlink', '_f0001') - childref = families[1].newChild(None, 'childref', None) - childref.newProp('hlink', '_i0003') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - families = ctxt.xpathEval("//g:family") - families[1].unlinkNode() - families[1].freeNode() - childof = ctxt.xpathEval("//g:person[@handle='_i0003']/g:childof")[0] - childof.setProp('hlink', '_f0000') - childref = families[0].newChild(None, 'childref', None) - childref.newProp('hlink', '_i0003') - self.do_case('I0000', 'I0002', self.basedoc, expect) + """Merge two people leading to merger of families that have children. + """ + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother", + namespaces={"g": NS_G})[0] + mother.getparent().remove(mother) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + ET.SubElement(persons[1], NSP + 'childof', hlink='_f0000') + families = input_ctxt.xpath("//g:family", + namespaces={"g": NS_G}) + ET.SubElement(families[0], NSP + 'childref', hlink='_i0001') + parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001') + ET.SubElement(families[1], NSP + 'father', hlink='_i0002') + ET.SubElement(persons[3], NSP + 'childof', hlink='_f0001') + ET.SubElement(families[1], NSP + 'childref', hlink='_i0003') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + families = expect.xpath("//g:family", + namespaces={"g": NS_G}) + families[1].getparent().remove(families[1]) + childof = expect.xpath("//g:person[@handle='_i0003']/g:childof", + namespaces={"g": NS_G})[0] + childof.attrib['hlink'] = '_f0000' + ET.SubElement(families[0], NSP + 'childref', hlink='_i0003') + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect) def test_childref_already(self): - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0] - mother.unlinkNode() - mother.freeNode() - persons = input_ctxt.xpathEval("//g:person") - childof = persons[1].newChild(None, 'childof', None) - childof.newProp('hlink', '_f0000') - childof = persons[1].newChild(None, 'childof', None) - childof.newProp('hlink', '_f0001') - families = input_ctxt.xpathEval("//g:family") - childref = families[0].newChild(None, 'childref', None) - childref.newProp('hlink', '_i0001') - parentin = persons[2].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - father = families[1].newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - childref = families[1].newChild(None, 'childref', None) - childref.newProp('hlink', '_i0001') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - families = ctxt.xpathEval("//g:family") - families[1].unlinkNode() - families[1].freeNode() - childof = ctxt.xpathEval("//g:person[@handle='_i0001']/g:childof") - childof[1].unlinkNode() - childof[1].freeNode() - self.do_case('I0000', 'I0002', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother", + namespaces={"g": NS_G})[0] + mother.getparent().remove(mother) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + ET.SubElement(persons[1], NSP + 'childof', hlink='_f0000') + ET.SubElement(persons[1], NSP + 'childof', hlink='_f0001') + families = input_ctxt.xpath("//g:family", + namespaces={"g": NS_G}) + ET.SubElement(families[0], NSP + 'childref', hlink='_i0001') + parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001') + ET.SubElement(families[1], NSP + 'father', hlink='_i0002') + ET.SubElement(families[1], NSP + 'childref', hlink='_i0001') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + families = expect.xpath("//g:family", + namespaces={"g": NS_G}) + families[1].getparent().remove(families[1]) + childof = expect.xpath("//g:person[@handle='_i0001']/g:childof", + namespaces={"g": NS_G}) + childof[1].getparent().remove(childof[1]) + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect) def test_ldsord(self): - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0] - mother.unlinkNode() - mother.freeNode() - persons = input_ctxt.xpathEval("//g:person") - parentin = persons[2].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - father = family.newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - ldsord = persons[3].newChild(None, 'lds_ord', None) - ldsord.newProp('type', 'sealed_to_parents') - sealedto = ldsord.newChild(None, 'sealed_to', None) - sealedto.newProp('hlink', '_f0001') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - families = ctxt.xpathEval("//g:family") - families[1].unlinkNode() - families[1].freeNode() - sealedto = ctxt.xpathEval("//g:sealed_to")[0] - sealedto.setProp('hlink', '_f0000') - self.do_case('I0000', 'I0002', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother", + namespaces={"g": NS_G})[0] + mother.getparent().remove(mother) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'father', hlink='_i0002') + ldsord = ET.SubElement(persons[3], NSP + 'lds_ord', + type='sealed_to_parents') + ET.SubElement(ldsord, NSP + 'sealed_to', hlink='_f0001') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', alt='1', + type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + families = expect.xpath("//g:family", + namespaces={"g": NS_G}) + families[1].getparent().remove(families[1]) + sealedto = expect.xpath("//g:sealed_to", + namespaces={"g": NS_G})[0] + sealedto.attrib['hlink'] = '_f0000' + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect) # This test fails because the assigment of family ids shifts F0000 to F0001 # and F0001 to F0002! - #def test_ldsord_cross(self): - # with XpathContext(self.basedoc) as input_ctxt: - # parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - # parentin.unlinkNode() - # parentin.freeNode() - # mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0] - # mother.unlinkNode() - # mother.freeNode() - # persons = input_ctxt.xpathEval("//g:person") - # parentin = persons[2].newChild(None, 'parentin', None) - # parentin.newProp('hlink', '_f0001') - # family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - # father = family.newChild(None, 'father', None) - # father.newProp('hlink', '_i0002') - # ldsord = persons[0].newChild(None, 'lds_ord', None) - # ldsord.newProp('type', 'sealed_to_parents') - # sealedto = ldsord.newChild(None, 'sealed_to', None) - # sealedto.newProp('hlink', '_f0001') - # with CopiedDoc(self.basedoc) as expect: - # with XpathContext(expect) as ctxt: - # persons = ctxt.xpathEval("//g:person") - # altname = persons[0].newChild(None, 'name', None) - # altname.newProp('alt', '1') - # altname.newProp('type', 'Birth Name') - # altname.newChild(None, 'surname', 'Person 2') - # ldsord = ctxt.xpathEval("//g:lds_ord")[0] - # altname.addNextSibling(ldsord) # restore order of elements - # attr = persons[0].newChild(None, 'attribute', None) - # attr.newProp('type', 'Merged Gramps ID') - # attr.newProp('value', 'I0002') - # parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - # attr.addNextSibling(parentref) # restore order of elements - # persons[2].unlinkNode() - # persons[2].freeNode() - # families = ctxt.xpathEval("//g:family") - # families[1].unlinkNode() - # families[1].freeNode() - # sealedto = ctxt.xpathEval("//g:sealed_to")[0] - # sealedto.setProp('hlink', '_f0000') - # self.do_case('I0000', 'I0002', self.basedoc, expect) + def test_ldsord_cross(self): + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother", + namespaces={"g": NS_G})[0] + mother.getparent().remove(mother) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'father', hlink='_i0002') + ldsord = ET.SubElement(persons[0], NSP + 'lds_ord', + type='sealed_to_parents') + ET.SubElement(ldsord, NSP + 'sealed_to', hlink='_f0001') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + ldsord = expect.xpath("//g:lds_ord", + namespaces={"g": NS_G})[0] + altname.addnext(ldsord) # restore order of elements + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + families = expect.xpath("//g:family", + namespaces={"g": NS_G}) + families[1].getparent().remove(families[1]) + sealedto = expect.xpath("//g:sealed_to", + namespaces={"g": NS_G})[0] + sealedto.attrib['hlink'] = '_f0000' + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect) def test_ldsord_self(self): - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0] - mother.unlinkNode() - mother.freeNode() - persons = input_ctxt.xpathEval("//g:person") - parentin = persons[2].newChild(None, 'parentin', None) - parentin.newProp('hlink', '_f0001') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - father = family.newChild(None, 'father', None) - father.newProp('hlink', '_i0002') - ldsord = persons[2].newChild(None, 'lds_ord', None) - ldsord.newProp('type', 'sealed_to_parents') - sealedto = ldsord.newChild(None, 'sealed_to', None) - sealedto.newProp('hlink', '_f0001') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - ldsord = ctxt.xpathEval("//g:lds_ord")[0] - altname.addNextSibling(ldsord) # restore order of elements - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - families = ctxt.xpathEval("//g:family") - families[1].unlinkNode() - families[1].freeNode() - sealedto = ctxt.xpathEval("//g:sealed_to")[0] - sealedto.setProp('hlink', '_f0000') - self.do_case('I0000', 'I0002', self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother", + namespaces={"g": NS_G})[0] + mother.getparent().remove(mother) + persons = input_ctxt.xpath("//g:person", + namespaces={"g": NS_G}) + parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'father', hlink='_i0002') + ldsord = ET.SubElement(persons[2], NSP + 'lds_ord', + type='sealed_to_parents') + ET.SubElement(ldsord, NSP + 'sealed_to', hlink='_f0001') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + ldsord = expect.xpath("//g:lds_ord", + namespaces={"g": NS_G})[0] + altname.addnext(ldsord) # restore order of elements + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + families = expect.xpath("//g:family", + namespaces={"g": NS_G}) + families[1].getparent().remove(families[1]) + sealedto = expect.xpath("//g:sealed_to", + namespaces={"g": NS_G})[0] + sealedto.attrib['hlink'] = '_f0000' + input_doc = ET.tostring(input_ctxt) + self.do_case('I0000', 'I0002', input_doc, expect) + #------------------------------------------------------------------------- # @@ -1877,366 +1855,356 @@ class FamilyMergeCheck(BaseMergeCheck): """ - self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None, - libxml2.XML_PARSE_NONET) + self.basedoc = bytes(bytearray(self.base_str + base_str, + encoding='utf-8')) def test_father_son_merge(self): """Merge two families where the fathers have a father-son relationship so that an error is raised.""" - with XpathContext(self.basedoc) as input_ctxt: - person = input_ctxt.xpathEval("//g:person[@handle='_i0002']")[0] - childof = person.newChild(None, 'childof', None) - childof.newProp('hlink', '_f0000') - family = input_ctxt.xpathEval("//g:family[@handle='_f0000']")[0] - childref = family.newChild(None, 'childref', None) - childref.newProp('hlink', '_i0002') - with CopiedDoc(self.basedoc) as expect: - self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', - self.basedoc, expect, test_error_str=_("A parent and " - "child cannot be merged. To merge these people, you " - "must first break the relationship between them.")) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + person = input_ctxt.xpath("//g:person[@handle='_i0002']", + namespaces={"g": NS_G})[0] + ET.SubElement(person, NSP + 'childof', hlink='_f0000') + family = input_ctxt.xpath("//g:family[@handle='_f0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'childref', hlink='_i0002') + input_doc = expect = ET.tostring(input_ctxt) + self.do_family_case( + 'F0000', 'F0001', 'i0000', 'i0001', input_doc, expect, + test_error_str=_("A parent and child cannot be merged. To merge " + "these people, you must first break the " + "relationship between them.")) def test_child_parent_merge_no_father(self): """Merge two families where the phoenix family has no father and - the father of the titanic family is a child of the phoenix family.""" - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0002']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - father = input_ctxt.xpathEval("//g:family[@handle='_f0001']/g:father")[0] - father.unlinkNode() - father.freeNode() - person = input_ctxt.xpathEval("//g:person[@handle='_i0000']")[0] - childof = person.newChild(None, 'childof', None) - childof.newProp('hlink', '_f0001') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - childref = family.newChild(None, 'childref', None) - childref.newProp('hlink', '_i0000') - with CopiedDoc(self.basedoc) as expect: - self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', - self.basedoc, expect, test_error_str=_("A parent and " - "child cannot be merged. To merge these people, you " - "must first break the relationship between them.")) + the father of the titanic family is a child of the phoenix family. + """ + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0002']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + father = input_ctxt.xpath("//g:family[@handle='_f0001']/g:father", + namespaces={"g": NS_G})[0] + father.getparent().remove(father) + person = input_ctxt.xpath("//g:person[@handle='_i0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(person, NSP + 'childof', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'childref', hlink='_i0000') + input_doc = expect = ET.tostring(input_ctxt) + self.do_family_case( + 'F0000', 'F0001', 'i0000', 'i0001', input_doc, expect, + test_error_str=_("A parent and child cannot be merged. To merge " + "these people, you must first break the " + "relationship between them.")) def test_child_parent_merge_no_father_swapped(self): """Merge two families where the phoenix family has no father and the father of the titanic family, which is the phoenix-father, is a child of the phoenix family.""" - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - father = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:father")[0] - father.unlinkNode() - father.freeNode() - person = input_ctxt.xpathEval("//g:person[@handle='_i0002']")[0] - childof = person.newChild(None, 'childof', None) - childof.newProp('hlink', '_f0000') - family = input_ctxt.xpathEval("//g:family[@handle='_f0000']")[0] - childref = family.newChild(None, 'childref', None) - childref.newProp('hlink', '_i0002') - with CopiedDoc(self.basedoc) as expect: - self.do_family_case('F0000', 'F0001', 'i0002', 'i0001', - self.basedoc, expect, test_error_str=_("A parent and " - "child cannot be merged. To merge these people, you " - "must first break the relationship between them.")) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + father = input_ctxt.xpath("//g:family[@handle='_f0000']/g:father", + namespaces={"g": NS_G})[0] + father.getparent().remove(father) + person = input_ctxt.xpath("//g:person[@handle='_i0002']", + namespaces={"g": NS_G})[0] + ET.SubElement(person, NSP + 'childof', hlink='_f0000') + family = input_ctxt.xpath("//g:family[@handle='_f0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'childref', hlink='_i0002') + input_doc = expect = ET.tostring(input_ctxt) + self.do_family_case( + 'F0000', 'F0001', 'i0002', 'i0001', input_doc, expect, + test_error_str=_("A parent and child cannot be merged. To merge " + "these people, you must first break the " + "relationship between them.")) def test_regular_merge(self): """Merge two families succesfully""" - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - altname = persons[1].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 3') - attr = persons[1].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0003') - parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[3].unlinkNode() - persons[3].freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + altname = ET.SubElement(persons[1], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 3' + attr = ET.SubElement(persons[1], NSP + 'attribute', + type='Merged Gramps ID', value='I0003') + parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[3].getparent().remove(persons[3]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', + self.basedoc, expect) def test_father_swapped(self): "Merge two families where the phoenix-father is of the titanic family." - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[2].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 0') - attr = persons[2].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0000') - parentref = ctxt.xpathEval("//g:person[@handle='_i0002']/g:parentin")[0] - parentref.setProp('hlink', '_f0000') - attr.addNextSibling(parentref) # restore order of elements - persons[0].unlinkNode() - persons[0].freeNode() - altname = persons[1].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 3') - attr = persons[1].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0003') - parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[3].unlinkNode() - persons[3].freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - father = ctxt.xpathEval("//g:family[@handle='_f0000']/g:father")[0] - father.setProp('hlink', '_i0002') - self.do_family_case('F0000', 'F0001', 'i0002', 'i0001', self.basedoc, expect) + expect = ET.fromstring(self.basedoc, parser=self.parser) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[2], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 0' + attr = ET.SubElement(persons[2], NSP + 'attribute', + type='Merged Gramps ID', value='I0000') + parentref = expect.xpath("//g:person[@handle='_i0002']/g:parentin", + namespaces={"g": NS_G})[0] + parentref.attrib['hlink'] = '_f0000' + attr.addnext(parentref) # restore order of elements + persons[0].getparent().remove(persons[0]) + altname = ET.SubElement(persons[1], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 3' + attr = ET.SubElement(persons[1], NSP + 'attribute', + type='Merged Gramps ID', value='I0003') + parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[3].getparent().remove(persons[3]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + father = expect.xpath("//g:family[@handle='_f0000']/g:father", + namespaces={"g": NS_G})[0] + father.attrib['hlink'] = '_i0002' + self.do_family_case('F0000', 'F0001', 'i0002', 'i0001', + self.basedoc, expect) #def test_mother_swapped(self): def test_no_father(self): """Merge two families, where one family has not father""" - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0002']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - father = input_ctxt.xpathEval("//g:family[@handle='_f0001']/g:father")[0] - father.unlinkNode() - father.freeNode() - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[1].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 3') - attr = persons[1].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0003') - parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[3].unlinkNode() - persons[3].freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', - self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0002']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + father = input_ctxt.xpath("//g:family[@handle='_f0001']/g:father", + namespaces={"g": NS_G})[0] + father.getparent().remove(father) + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[1], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 3' + attr = ET.SubElement(persons[1], NSP + 'attribute', + type='Merged Gramps ID', value='I0003') + parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[3].getparent().remove(persons[3]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + input_doc = ET.tostring(input_ctxt) + self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', + input_doc, expect) def test_no_mother_swapped(self): """Merge two families where one family has no mother and the phoenix-mother is from the titanic family.""" - with XpathContext(self.basedoc) as input_ctxt: - parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - parentin.unlinkNode() - parentin.freeNode() - mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0] - mother.unlinkNode() - mother.freeNode() - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - parentin = ctxt.xpathEval("//g:person[@handle='_i0003']/g:parentin")[0] - parentin.setProp('hlink', '_f0000') - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0000']")[0] - mother = family.newChild(None, 'mother', None) - mother.newProp('hlink', '_i0003') - self.do_family_case('F0000', 'F0001', 'i0000', 'i0003', - self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.getparent().remove(parentin) + mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother", + namespaces={"g": NS_G})[0] + mother.getparent().remove(mother) + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + parentin = expect.xpath("//g:person[@handle='_i0003']/g:parentin", + namespaces={"g": NS_G})[0] + parentin.attrib['hlink'] = '_f0000' + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + family = expect.xpath("//g:family[@handle='_f0000']", + namespaces={"g": NS_G})[0] + mother = ET.SubElement(family, NSP + 'mother', hlink='_i0003') + input_doc = ET.tostring(input_ctxt) + self.do_family_case('F0000', 'F0001', 'i0000', 'i0003', + input_doc, expect) #def test_no_parents(self): def test_childref_notyet(self): """Merge two families with non-duplicate child references.""" - with XpathContext(self.basedoc) as input_ctxt: - people = input_ctxt.xpathEval("//g:people")[0] - person = people.newChild(None, 'person', None) - person.newProp('handle', '_i0004') - person.newProp('id', '_I0004') - person.newChild(None, 'gender', 'M') - name = person.newChild(None, 'name', None) - name.newProp('type', 'Birth Name') - name.newChild(None, 'surname', 'Person 4') - childof = person.newChild(None, 'childof', None) - childof.newProp('hlink', '_f0001') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - childref = family.newChild(None, 'childref', None) - childref.newProp('hlink', '_i0004') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - altname = persons[1].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 3') - attr = persons[1].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0003') - parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[3].unlinkNode() - persons[3].freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - childof = ctxt.xpathEval("//g:person[@handle='_i0004']/g:childof")[0] - childof.setProp('hlink', '_f0000') - family = ctxt.xpathEval("//g:family[@handle='_f0000']")[0] - childref = family.newChild(None, 'childref', None) - childref.newProp('hlink', '_i0004') - self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', - self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + people = input_ctxt.xpath("//g:people", + namespaces={"g": NS_G})[0] + person = ET.SubElement(people, NSP + 'person', + handle='_i0004', id='_I0004') + ET.SubElement(person, NSP + 'gender').text = 'M' + name = ET.SubElement(person, NSP + 'name', type='Birth Name') + ET.SubElement(name, NSP + 'surname').text = 'Person 4' + ET.SubElement(person, NSP + 'childof', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'childref', hlink='_i0004') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + altname = ET.SubElement(persons[1], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 3' + attr = ET.SubElement(persons[1], NSP + 'attribute', + type='Merged Gramps ID', value='I0003') + parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[3].getparent().remove(persons[3]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + childof = expect.xpath("//g:person[@handle='_i0004']/g:childof", + namespaces={"g": NS_G})[0] + childof.attrib['hlink'] = '_f0000' + family = expect.xpath("//g:family[@handle='_f0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'childref', hlink='_i0004') + input_doc = ET.tostring(input_ctxt) + self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', + input_doc, expect) def test_childref_already(self): """Merge two families with duplicate child references.""" - with XpathContext(self.basedoc) as input_ctxt: - people = input_ctxt.xpathEval("//g:people")[0] - person = people.newChild(None, 'person', None) - person.newProp('handle', '_i0004') - person.newProp('id', '_I0004') - person.newChild(None, 'gender', 'M') - name = person.newChild(None, 'name', None) - name.newProp('type', 'Birth Name') - name.newChild(None, 'surname', 'Person 4') - childof = person.newChild(None, 'childof', None) - childof.newProp('hlink', '_f0000') - childof = person.newChild(None, 'childof', None) - childof.newProp('hlink', '_f0001') - family = input_ctxt.xpathEval("//g:family[@handle='_f0000']")[0] - childref = family.newChild(None, 'childref', None) - childref.newProp('hlink', '_i0004') - family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - childref = family.newChild(None, 'childref', None) - childref.newProp('hlink', '_i0004') - with CopiedDoc(self.basedoc) as expect: - with XpathContext(expect) as ctxt: - persons = ctxt.xpathEval("//g:person") - altname = persons[0].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 2') - attr = persons[0].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0002') - parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[2].unlinkNode() - persons[2].freeNode() - altname = persons[1].newChild(None, 'name', None) - altname.newProp('alt', '1') - altname.newProp('type', 'Birth Name') - altname.newChild(None, 'surname', 'Person 3') - attr = persons[1].newChild(None, 'attribute', None) - attr.newProp('type', 'Merged Gramps ID') - attr.newProp('value', 'I0003') - parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - attr.addNextSibling(parentref) # restore order of elements - persons[3].unlinkNode() - persons[3].freeNode() - family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - family.unlinkNode() - family.freeNode() - childof = ctxt.xpathEval("//g:person[@handle='_i0004']/g:childof")[1] - childof.unlinkNode() - childof.freeNode() - self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', - self.basedoc, expect) + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + people = input_ctxt.xpath("//g:people", + namespaces={"g": NS_G})[0] + person = ET.SubElement(people, NSP + 'person', + handle='_i0004', id='_I0004') + ET.SubElement(person, NSP + 'gender').text = 'M' + name = ET.SubElement(person, NSP + 'name', type='Birth Name') + ET.SubElement(name, NSP + 'surname').text = 'Person 4' + ET.SubElement(person, NSP + 'childof', hlink='_f0000') + ET.SubElement(person, NSP + 'childof', hlink='_f0001') + family = input_ctxt.xpath("//g:family[@handle='_f0000']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'childref', hlink='_i0004') + family = input_ctxt.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + ET.SubElement(family, NSP + 'childref', hlink='_i0004') + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + altname = ET.SubElement(persons[1], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 3' + attr = ET.SubElement(persons[1], NSP + 'attribute', + type='Merged Gramps ID', value='I0003') + parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[3].getparent().remove(persons[3]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + childof = expect.xpath("//g:person[@handle='_i0004']/g:childof", + namespaces={"g": NS_G})[1] + childof.getparent().remove(childof) + input_doc = ET.tostring(input_ctxt) + self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', + input_doc, expect) # this test fails because the families get IDs F0001 and F0002! - #def test_ldsord(self): - # """Merge two families where one person has a reference to the - # titanic family.""" - # with XpathContext(self.basedoc) as input_ctxt: - # person = input_ctxt.xpathEval("//g:person[@handle='_i0000']")[0] - # ldsord = person.newChild(None, 'lds_ord', None) - # ldsord.newProp('type', 'sealed_to_parents') - # sealedto = ldsord.newChild(None, 'sealed_to', None) - # sealedto.newProp('hlink', '_f0001') - # parentin = input_ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - # ldsord.addNextSibling(parentin) - # with CopiedDoc(self.basedoc) as expect: - # with XpathContext(expect) as ctxt: - # persons = ctxt.xpathEval("//g:person") - # altname = persons[0].newChild(None, 'name', None) - # altname.newProp('alt', '1') - # altname.newProp('type', 'Birth Name') - # altname.newChild(None, 'surname', 'Person 2') - # ldsord = ctxt.xpathEval("//g:lds_ord")[0] - # altname.addNextSibling(ldsord) # restore order of elements - # attr = persons[0].newChild(None, 'attribute', None) - # attr.newProp('type', 'Merged Gramps ID') - # attr.newProp('value', 'I0002') - # parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0] - # attr.addNextSibling(parentref) # restore order of elements - # persons[2].unlinkNode() - # persons[2].freeNode() - # altname = persons[1].newChild(None, 'name', None) - # altname.newProp('alt', '1') - # altname.newProp('type', 'Birth Name') - # altname.newChild(None, 'surname', 'Person 3') - # attr = persons[1].newChild(None, 'attribute', None) - # attr.newProp('type', 'Merged Gramps ID') - # attr.newProp('value', 'I0003') - # parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0] - # attr.addNextSibling(parentref) # restore order of elements - # persons[3].unlinkNode() - # persons[3].freeNode() - # family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0] - # family.unlinkNode() - # family.freeNode() - # sealedto = ctxt.xpathEval("//g:sealed_to")[0] - # sealedto.setProp('hlink', '_f0000') - # self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', - # self.basedoc, expect) + def test_ldsord(self): + """Merge two families where one person has a reference to the + titanic family.""" + input_ctxt = ET.fromstring(self.basedoc, parser=self.parser) + person = input_ctxt.xpath("//g:person[@handle='_i0000']", + namespaces={"g": NS_G})[0] + ldsord = ET.SubElement(person, NSP + 'lds_ord', + type='sealed_to_parents') + ET.SubElement(ldsord, NSP + 'sealed_to', hlink='_f0001') + parentin = input_ctxt.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + ldsord.addnext(parentin) + expect = copy.deepcopy(input_ctxt) + persons = expect.xpath("//g:person", + namespaces={"g": NS_G}) + altname = ET.SubElement(persons[0], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 2' + ldsord = expect.xpath("//g:lds_ord", + namespaces={"g": NS_G})[0] + altname.addnext(ldsord) # restore order of elements + attr = ET.SubElement(persons[0], NSP + 'attribute', + type='Merged Gramps ID', value='I0002') + parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[2].getparent().remove(persons[2]) + altname = ET.SubElement(persons[1], NSP + 'name', + alt='1', type='Birth Name') + ET.SubElement(altname, NSP + 'surname').text = 'Person 3' + attr = ET.SubElement(persons[1], NSP + 'attribute', + type='Merged Gramps ID', value='I0003') + parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin", + namespaces={"g": NS_G})[0] + attr.addnext(parentref) # restore order of elements + persons[3].getparent().remove(persons[3]) + family = expect.xpath("//g:family[@handle='_f0001']", + namespaces={"g": NS_G})[0] + family.getparent().remove(family) + sealedto = expect.xpath("//g:sealed_to", + namespaces={"g": NS_G})[0] + sealedto.attrib['hlink'] = '_f0000' + input_doc = ET.tostring(input_ctxt) + self.do_family_case('F0000', 'F0001', 'i0000', 'i0001', + input_doc, expect) if __name__ == "__main__": import sys if not HAS_CLIMERGE: - print('This program needs the third party "CliMerge" plugin.', file=sys.stderr) + print('This program needs the third party "CliMerge" plugin.', + file=sys.stderr) sys.exit(1) if not HAS_EXPORTRAW: - print('This program needs the third party "ExportRaw" plugin.', file=sys.stderr) + print('This program needs the third party "ExportRaw" plugin.', + file=sys.stderr) sys.exit(1) unittest.main() diff --git a/gramps/plugins/export/exportxml.py b/gramps/plugins/export/exportxml.py index a97f99c80..e73d412a8 100644 --- a/gramps/plugins/export/exportxml.py +++ b/gramps/plugins/export/exportxml.py @@ -126,7 +126,10 @@ class GrampsXmlWriter(UpdateCallback): """ if filename == '-': import sys - g = sys.stdout.buffer + try: + g = sys.stdout.buffer + except: + g = sys.stdout self.compress = False else: base = os.path.dirname(filename) diff --git a/gramps/plugins/importer/importxml.py b/gramps/plugins/importer/importxml.py index fc245261b..6dfdee06d 100644 --- a/gramps/plugins/importer/importxml.py +++ b/gramps/plugins/importer/importxml.py @@ -399,7 +399,10 @@ class ImportOpenFileContextManager: def __enter__(self): if self.filename == '-': - self.filehandle = sys.stdin.buffer + try: + self.filehandle = sys.stdin.buffer + except: + self.filehandle = sys.stdin else: self.filehandle = self.open_file(self.filename) return self.filehandle diff --git a/gramps/test/test_util.py b/gramps/test/test_util.py index 75705f5d6..9ca18f152 100644 --- a/gramps/test/test_util.py +++ b/gramps/test/test_util.py @@ -221,14 +221,14 @@ def new_exit(edit_code=None): raise SystemExit() @contextlib.contextmanager -def capture(stdin): +def capture(stdin, bytesio=False): oldout, olderr = sys.stdout, sys.stderr oldexit = sys.exit if stdin: oldin = sys.stdin sys.stdin = stdin try: - output = [StringIO(), StringIO()] + output = [BytesIO() if bytesio else StringIO(), StringIO()] sys.stdout, sys.stderr = output sys.exit = new_exit yield output @@ -252,8 +252,8 @@ class Gramps: self.climanager = CLIManager(self.dbstate, setloader=True, user=self.user) self.clidbmanager = CLIDbManager(self.dbstate) - def run(self, *args, stdin=None): - with capture(stdin) as output: + def run(self, *args, stdin=None, bytesio=False): + with capture(stdin, bytesio=bytesio) as output: #load the plugins self.climanager.do_reg_plugins(self.dbstate, uistate=None) # handle the arguments