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>
|
||||
* src/test/test_util.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