added Gedcom read testing util and 1st try at testing

svn: r9500
This commit is contained in:
James G Sack 2007-12-14 08:27:52 +00:00
parent 726bb4e8b8
commit cc81822eb0
4 changed files with 335 additions and 0 deletions

View File

@ -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

View 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
View 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===

View 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()