added Gedcom read testing util and 1st try at testing
svn: r9500
This commit is contained in:
parent
726bb4e8b8
commit
cc81822eb0
@ -1,3 +1,9 @@
|
|||||||
|
2007-12-13 Jim Sack <jgsack@san.rr.com>
|
||||||
|
src/test/gedread_util.py : added to support testing
|
||||||
|
src/test/test/gedread_util_test.py : added to test util above
|
||||||
|
src/GrampsDbUtils/test/GR_test.py : added 1st try at testing
|
||||||
|
first try at some gedcon read testing
|
||||||
|
|
||||||
2007-12-13 Jim Sack <jgsack@san.rr.com>
|
2007-12-13 Jim Sack <jgsack@san.rr.com>
|
||||||
* src/test/test_util.py
|
* src/test/test_util.py
|
||||||
* src/test/test/test_util_test.py
|
* src/test/test/test_util_test.py
|
||||||
|
146
src/GrampsDbUtils/test/GR_test.py
Normal file
146
src/GrampsDbUtils/test/GR_test.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
""" GR_test.py
|
||||||
|
|
||||||
|
This is a first try at some gedcom read testing that does not
|
||||||
|
require running a gramps CLI
|
||||||
|
|
||||||
|
The biggest difficulty is that every test fragment needs custom
|
||||||
|
test code. Maybe that's unavoidable, and the best that can be
|
||||||
|
done is to group similar tests together, so that setUp can be
|
||||||
|
shared.
|
||||||
|
|
||||||
|
Maybe more can be shared: one commonly used test recipe is
|
||||||
|
to develop a data structure that can be looped over to test
|
||||||
|
similar fragments with the same piece of test code, putting
|
||||||
|
fragments and possible control or other validation information
|
||||||
|
in the data structure.
|
||||||
|
|
||||||
|
A controlling principle for such structures is that they should be
|
||||||
|
designed for maximum ease (and intuitiveness) of data declaration
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys, os, os.path as op
|
||||||
|
|
||||||
|
import unittest as U
|
||||||
|
import re
|
||||||
|
|
||||||
|
from test import test_util as tu
|
||||||
|
from test import gedread_util as gr
|
||||||
|
|
||||||
|
|
||||||
|
# NoteSource_frag
|
||||||
|
# tests structure: NOTE > SOUR
|
||||||
|
# using the 2 formats of the SOUR element
|
||||||
|
# bug #(?) does not properly ignore the SOUR
|
||||||
|
# test by looking for warning messages resulting
|
||||||
|
# from parse_record seeing the non-skipped SOUR
|
||||||
|
#
|
||||||
|
# SB: the NOTE data should contain the SOUR or xref
|
||||||
|
# but this is NYI (SOUR is ignored within NOTE)
|
||||||
|
# -----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# numcheck based testing
|
||||||
|
# verifies the number of db items via a get_number_of_X() call
|
||||||
|
# returns an error string or None
|
||||||
|
#
|
||||||
|
# ? candidate for inclusion in gedread_util.py
|
||||||
|
#
|
||||||
|
class nc():
|
||||||
|
"""nc object -- creates a numcheck function
|
||||||
|
|
||||||
|
instantiate a nc object as follows
|
||||||
|
c = nc("people", 4)
|
||||||
|
and call, passing the database, as follows
|
||||||
|
err = c(db)
|
||||||
|
which will check for exactly 4 people in the db
|
||||||
|
and return a displayable message on error, else None
|
||||||
|
|
||||||
|
NB: name _must_ match the X names in db get_number_of_X
|
||||||
|
"""
|
||||||
|
def dbncheck(s, dbcall):
|
||||||
|
err = None
|
||||||
|
got = dbcall()
|
||||||
|
if not got == s.num:
|
||||||
|
err = "%s: got %d, expected %d" % (s.name, got, s.num)
|
||||||
|
return err
|
||||||
|
def __init__(s, name, num):
|
||||||
|
s.name = name
|
||||||
|
s.num = num
|
||||||
|
s.getname = "get_number_of_" + name
|
||||||
|
def __call__(s, db):
|
||||||
|
dbcall = getattr(db,s.getname)
|
||||||
|
s.dbncheck(dbcall)
|
||||||
|
|
||||||
|
class fnci():
|
||||||
|
"""fnci (frag-numcheckset item) is a data container for:
|
||||||
|
a fragment of gedcom
|
||||||
|
a sequence of nc items to check
|
||||||
|
"""
|
||||||
|
def __init__(s, frag, ncset):
|
||||||
|
s.frag = frag
|
||||||
|
s.ncset = ncset
|
||||||
|
|
||||||
|
# test data table for Test1.test1a_numchecks
|
||||||
|
fnumchecks = (
|
||||||
|
fnci("""0 @N1@ NOTE Note containing embedded source
|
||||||
|
1 SOUR embedded source""",
|
||||||
|
(nc("notes", 1),)
|
||||||
|
),
|
||||||
|
fnci("""0 @N2@ NOTE Note containing referenced source
|
||||||
|
1 SOUR @SOUR1@
|
||||||
|
0 @SOUR1@ SOUR
|
||||||
|
1 TITL Phoney source title""",
|
||||||
|
(nc("notes", 1), nc("sources",1),)
|
||||||
|
),
|
||||||
|
)#end fnumchecks
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# ? candidate for inclusion in test_util.py
|
||||||
|
#
|
||||||
|
def _checklog(tlogger, pat=None):
|
||||||
|
# look for absence of specific messages in log
|
||||||
|
matches = 0
|
||||||
|
ltext = tlogger.logfile_getlines()
|
||||||
|
if ltext:
|
||||||
|
if pat is None:
|
||||||
|
matches = len(ltext)
|
||||||
|
else:
|
||||||
|
pat = re.compile(pat)
|
||||||
|
for l in ltext:
|
||||||
|
match = re.match(pat, l)
|
||||||
|
if match:
|
||||||
|
matches += 1
|
||||||
|
# debugging
|
||||||
|
print "(%d) %r" % (matches, match)
|
||||||
|
return matches
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Test1(U.TestCase):
|
||||||
|
def setUp(s):
|
||||||
|
# make a test subdir and compose some pathnames
|
||||||
|
s.tdir = tu.make_subdir("RG_test")
|
||||||
|
s.tdb = op.join(s.tdir,"test_db")
|
||||||
|
s.ifil = op.join(s.tdir,"test_in.ged")
|
||||||
|
s.lfil = op.join(s.tdir,"test.log")
|
||||||
|
|
||||||
|
def test1a_numchecks(s):
|
||||||
|
tl = tu.TestLogger()
|
||||||
|
for i,f in enumerate(fnumchecks):
|
||||||
|
gr.make_gedcom_input(s.ifil, f.frag)
|
||||||
|
db = gr.create_empty_db(s.tdb)
|
||||||
|
tl.logfile_init(s.lfil)
|
||||||
|
gr.gread(db,s.ifil)
|
||||||
|
errs = _checklog(tl, r"Line \d+")
|
||||||
|
s.assertEquals(errs, 0,
|
||||||
|
"ncset(%d): got %d unexpected log messages" %
|
||||||
|
(i,errs))
|
||||||
|
# ok, no log error message, check db counts
|
||||||
|
for call in f.ncset:
|
||||||
|
err = call(db)
|
||||||
|
s.assertFalse(err, err)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
U.main()
|
128
src/test/gedread_util.py
Normal file
128
src/test/gedread_util.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
"""unittest support utilities for reading gedcom
|
||||||
|
|
||||||
|
see gedread_test.py for sample usage
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from test import test_util as tu
|
||||||
|
from GrampsDbUtils import _ReadGedcom as RG
|
||||||
|
from GrampsDbUtils import _GedcomStageOne as S1
|
||||||
|
from GrampsDbUtils import _GedcomParse as GP
|
||||||
|
import DbState
|
||||||
|
import gen.db
|
||||||
|
import Config
|
||||||
|
|
||||||
|
# extraneous leading newlines do not seem to cause problems
|
||||||
|
# (and actually make it convenient reading the test files!)
|
||||||
|
# future: may need to remove such lines here if problems develop
|
||||||
|
|
||||||
|
# These ged-chunks provide/observe the following requirements
|
||||||
|
# - minimum required header elements
|
||||||
|
# - a trailer
|
||||||
|
# - and one record (spec minimum), using a SUBM
|
||||||
|
# Note: not all specified requirements seem strongly enforcced
|
||||||
|
# eg: at least one record, also nonexistent references seem
|
||||||
|
# ok by design, so the SUBM could have been missing
|
||||||
|
# Also note that the 'tail' containing the SUBM record referenced
|
||||||
|
# in the header causes a line of console output because we
|
||||||
|
# presently do not process SUBM records at all
|
||||||
|
# (seems like a bug to me -- to be dealt with later)
|
||||||
|
# ---------------------------------------------------------------
|
||||||
|
|
||||||
|
# _head is presently simply a header with minimum content
|
||||||
|
_head ="""
|
||||||
|
0 HEAD
|
||||||
|
1 SOUR test_gedread_System_ID
|
||||||
|
1 SUBM @SUBM1@
|
||||||
|
1 GEDC
|
||||||
|
2 VERS 5.5
|
||||||
|
2 FORM LINEAGE-LINKED
|
||||||
|
1 CHAR ASCII
|
||||||
|
"""
|
||||||
|
|
||||||
|
# _tail is presently a single (SUBM) record plus the trailer
|
||||||
|
# to satisfy the "one or more records" in the spec
|
||||||
|
# it also provides a target for the xref in the header
|
||||||
|
_tail = """
|
||||||
|
0 @SUBM1@ SUBM
|
||||||
|
1 NAME test /gedread/
|
||||||
|
0 TRLR
|
||||||
|
"""
|
||||||
|
|
||||||
|
def make_gedcom_input(gfile, fragment):
|
||||||
|
"""create gedcom file with 'fragment' between our head & tail
|
||||||
|
|
||||||
|
fragment would normally be 1 or more complete records
|
||||||
|
fragment could be an empty string ("")
|
||||||
|
|
||||||
|
"""
|
||||||
|
fh = open(gfile,"w")
|
||||||
|
for txt in (_head, fragment, _tail):
|
||||||
|
fh.write(txt)
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
|
||||||
|
# code patterned after contents of ReadGedcom.import2,
|
||||||
|
# but avoiding the occurrence of a popup DialogError.
|
||||||
|
# -------------------------------------------------------
|
||||||
|
def gread(db, fname):
|
||||||
|
"""read gedcom file into a test db
|
||||||
|
|
||||||
|
NB: test modules may want to consider also, the simplified
|
||||||
|
test logging (from test_util) which is especially helpful
|
||||||
|
for testing gedcom support
|
||||||
|
|
||||||
|
"""
|
||||||
|
cback = None
|
||||||
|
DEF_SRC = False
|
||||||
|
ifile = open(fname,"rU")
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
s1 = RG.StageOne(ifile)
|
||||||
|
s1.parse()
|
||||||
|
except Exception,e:
|
||||||
|
raise tu.TestError("stage1 error %r" % e)
|
||||||
|
|
||||||
|
useTrans = False
|
||||||
|
ifile.seek(0)
|
||||||
|
try:
|
||||||
|
gp = RG.GedcomParser(db, ifile, fname, cback, s1, DEF_SRC)
|
||||||
|
except Exception, e:
|
||||||
|
raise tu.TestError("parser init error %r" % e)
|
||||||
|
|
||||||
|
##ro = db.readonly
|
||||||
|
##db.readonly = False # why?
|
||||||
|
try:
|
||||||
|
gp.parse_gedcom_file(useTrans)
|
||||||
|
err = ""
|
||||||
|
except Exception, e:
|
||||||
|
raise tu.TestError("parse error %r" %e)
|
||||||
|
##db.readonly = ro
|
||||||
|
finally:
|
||||||
|
ifile.close()
|
||||||
|
|
||||||
|
|
||||||
|
# test db creation
|
||||||
|
#
|
||||||
|
# This may deserve it's own module, but for now it is only used here
|
||||||
|
#
|
||||||
|
# state doesn't seem to be necessary for testing
|
||||||
|
# let's try just returning the db
|
||||||
|
#----------------------------------------------------
|
||||||
|
def create_empty_db(dbpath):
|
||||||
|
"""create an empty db for the test caller"""
|
||||||
|
state = DbState.DbState()
|
||||||
|
dbclass = gen.db.dbdir.GrampsDBDir
|
||||||
|
state.change_database(dbclass(Config.get(Config.TRANSACTIONS)))
|
||||||
|
# create empty db (files) via load()
|
||||||
|
cback = None
|
||||||
|
mode = "rw"
|
||||||
|
if os.path.isdir(dbpath):
|
||||||
|
shutil.rmtree(dbpath)
|
||||||
|
state.db.load(dbpath, cback, mode)
|
||||||
|
return state.db
|
||||||
|
|
||||||
|
#===eof===
|
55
src/test/test/gedread_util_test.py
Normal file
55
src/test/test/gedread_util_test.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"""test for test/gedread_util.py
|
||||||
|
|
||||||
|
also illustrates basic use
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import unittest as U
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from test import test_util as tu
|
||||||
|
from test import gedread_util as gr
|
||||||
|
|
||||||
|
|
||||||
|
class Test(U.TestCase):
|
||||||
|
def setUp(s):
|
||||||
|
# make a dir to hold an input gedcom file
|
||||||
|
s.tdir = tu.make_subdir("gr_test")
|
||||||
|
|
||||||
|
def test1(s):
|
||||||
|
prec="""
|
||||||
|
0 @I1@ INDI
|
||||||
|
1 NAME GedRead TEST /Person/
|
||||||
|
"""
|
||||||
|
|
||||||
|
# create a gedcom input file
|
||||||
|
# from canned head/tail -- see gedread_util
|
||||||
|
infil = os.path.join(s.tdir,"test_in.ged")
|
||||||
|
gr.make_gedcom_input(infil, prec)
|
||||||
|
s.assertTrue(os.path.isfile(infil),
|
||||||
|
"create input file %s" % infil)
|
||||||
|
|
||||||
|
# create an empty database
|
||||||
|
dbpath = os.path.join(s.tdir,"test_db")
|
||||||
|
db = gr.create_empty_db(dbpath)
|
||||||
|
s.assertTrue(os.path.isdir(dbpath),
|
||||||
|
"create database (dir) %s" % dbpath)
|
||||||
|
|
||||||
|
# create logfile to test for read log-messages
|
||||||
|
# (note: uses recently added test_util
|
||||||
|
log = os.path.join(s.tdir, "test_log")
|
||||||
|
tl = tu.TestLogger()
|
||||||
|
tl.logfile_init(log)
|
||||||
|
# now read the gedcom
|
||||||
|
gr.gread(db, infil)
|
||||||
|
logging.warn("nothing here")
|
||||||
|
loglines = tl.logfile_getlines()
|
||||||
|
s.assertEquals(len(loglines),1,
|
||||||
|
"log has no unexpected content")
|
||||||
|
# verify one person in database
|
||||||
|
np = db.get_number_of_people()
|
||||||
|
s.assertEquals(np,1,
|
||||||
|
tu.msg(np,1, "db has exactly one person"))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
U.main()
|
Loading…
Reference in New Issue
Block a user