Commits

Matt Chaput committed 3493100

Converted tests to nose.

Comments (0)

Files changed (33)

src/whoosh/support/testing.py

 import shutil
 import tempfile
+from functools import wraps
 
 from whoosh.filedb.filestore import FileStorage
 
         return FileStorage(self.dir)
     
     def __exit__(self, exc_type, exc_val, exc_tb):
-        if exc_type is not None:
-            if exc_type not in self.suppress:
-                print "Temp dir=", self.dir
-                return False
-        
         if not self.keepdir:
             try:
                 shutil.rmtree(self.dir)
             except OSError, e:
                 print "Can't remove temp dir: " + str(e)
+        
+        if exc_type is not None:
+            if self.keepdir:
+                print "Temp dir=", self.dir
+            if exc_type not in self.suppress:
+                return False
 
 
 class TempIndex(TempStorage):
         return fstore.create_index(self.schema, indexname=self.basename)
 
 
+def skip_if(cond):
+    """A Nose test decorator that skips the decorated test if the given
+    function returns True at runtime.
+    """
+    
+    def decorating_function(testfn):
+        @wraps(testfn)
+        def wrapper(*args, **kwargs):
+            if cond():
+                from nose.plugins.skip import SkipTest
+                raise SkipTest
+            else:
+                return testfn(*args, **kwargs)
+        
+        return wrapper
+    return decorating_function
+    
+
+def skip_if_unavailable(modulename):
+    """A Nose test decorator that only runs the decorated test if a module
+    can be imported::
+    
+        @skip_if_unavailable("multiprocessing")
+        def test_mp():
+    
+    Raises ``SkipTest`` if the module cannot be imported.
+    """
+    
+    def cantimport():
+        try:
+            __import__(modulename)
+        except ImportError:
+            return True
+        else:
+            return False
+        
+    return skip_if(cantimport)
+
+
+

stress/test_bigindex.py

 from __future__ import with_statement
-import unittest
 
 import random
 
 from whoosh.util import now
 
 
-class Test(unittest.TestCase):
-    def test_20000_single(self):
-        sc = fields.Schema(id=fields.ID(stored=True), text=fields.TEXT)
-        with TempIndex(sc, "20000single") as ix:
-            domain = ["alfa", "bravo", "charlie", "delta", "echo", "foxtrot",
-                      "golf", "hotel", "india", "juliet", "kilo", "lima"]
-            
-            t = now()
-            for i in xrange(20000):
+def test_20000_single():
+    sc = fields.Schema(id=fields.ID(stored=True), text=fields.TEXT)
+    with TempIndex(sc, "20000single") as ix:
+        domain = ["alfa", "bravo", "charlie", "delta", "echo", "foxtrot",
+                  "golf", "hotel", "india", "juliet", "kilo", "lima"]
+        
+        t = now()
+        for i in xrange(20000):
+            w = ix.writer()
+            w.add_document(id=unicode(i), text=u" ".join(random.sample(domain, 5)))
+            w.commit()
+        print "Write single:", now() - t
+        
+        t = now()
+        ix.optimize()
+        print "Optimize single:", now() - t
+
+def test_20000_buffered():
+    from whoosh.writing import BufferedWriter
+    
+    sc = fields.Schema(id=fields.ID(stored=True), text=fields.TEXT)
+    with TempIndex(sc, "20000buffered") as ix:
+        domain = ["alfa", "bravo", "charlie", "delta", "echo", "foxtrot",
+                  "golf", "hotel", "india", "juliet", "kilo", "lima"]
+        
+        t = now()
+        w = BufferedWriter(ix, limit=100, period=None)
+        for i in xrange(20000):
+            w.add_document(id=unicode(i),
+                           text = u" ".join(random.sample(domain, 5)))
+        w.close()
+        print "Write buffered:", now() - t
+        
+        t = now()
+        ix.optimize()
+        print "Optimize buffered:", now() - t
+        
+def test_20000_batch():
+    sc = fields.Schema(id=fields.ID(stored=True), text=fields.TEXT)
+    with TempIndex(sc, "20000batch") as ix:
+        domain = ["alfa", "bravo", "charlie", "delta", "echo", "foxtrot",
+                  "golf", "hotel", "india", "juliet", "kilo", "lima"]
+        
+        t = now()
+        w = ix.writer()
+        for i in xrange(20000):
+            w.add_document(id=unicode(i),
+                           text = u" ".join(random.sample(domain, 5)))
+            if not i % 100:
+                w.commit()
                 w = ix.writer()
-                w.add_document(id=unicode(i), text=u" ".join(random.sample(domain, 5)))
-                w.commit()
-            print "Write single:", now() - t
-            
-            t = now()
-            ix.optimize()
-            print "Optimize single:", now() - t
+        w.commit()
+        print "Write batch:", now() - t
+        
+        t = now()
+        ix.optimize()
+        print "Optimize batch:", now() - t
 
-    def test_20000_buffered(self):
-        from whoosh.writing import BufferedWriter
-        
-        sc = fields.Schema(id=fields.ID(stored=True), text=fields.TEXT)
-        with TempIndex(sc, "20000buffered") as ix:
-            domain = ["alfa", "bravo", "charlie", "delta", "echo", "foxtrot",
-                      "golf", "hotel", "india", "juliet", "kilo", "lima"]
-            
-            t = now()
-            w = BufferedWriter(ix, limit=100, period=None)
-            for i in xrange(20000):
-                w.add_document(id=unicode(i),
-                               text = u" ".join(random.sample(domain, 5)))
-            w.close()
-            print "Write buffered:", now() - t
-            
-            t = now()
-            ix.optimize()
-            print "Optimize buffered:", now() - t
-            
-    def test_20000_batch(self):
-        sc = fields.Schema(id=fields.ID(stored=True), text=fields.TEXT)
-        with TempIndex(sc, "20000batch") as ix:
-            domain = ["alfa", "bravo", "charlie", "delta", "echo", "foxtrot",
-                      "golf", "hotel", "india", "juliet", "kilo", "lima"]
-            
-            t = now()
-            w = ix.writer()
-            for i in xrange(20000):
-                w.add_document(id=unicode(i),
-                               text = u" ".join(random.sample(domain, 5)))
-                if not i % 100:
-                    w.commit()
-                    w = ix.writer()
-            w.commit()
-            print "Write batch:", now() - t
-            
-            t = now()
-            ix.optimize()
-            print "Optimize batch:", now() - t
 
 
-
-if __name__ == "__main__":
-    unittest.main()

stress/test_bigsort.py

-import unittest
-
 import os.path, random, shutil
 from datetime import datetime
 
-from whoosh import fields, index, query, scoring
+from whoosh import fields, index, query
 from whoosh.util import now
 
 
-class TestSorting(unittest.TestCase):
-    def test_bigsort(self):
-        times = 30000
-        dirname = "testindex"
-        
-        df = fields.DATETIME(stored=True)
-        schema = fields.Schema(id=fields.ID(stored=True), date=df)
-        
-        if os.path.exists(dirname):
-            shutil.rmtree(dirname)
-        os.mkdir(dirname)
-        ix = index.create_in(dirname, schema)
-        
-        print "Writing..."
-        t = now()
-        w = ix.writer(limitmb=512)
-        for i in xrange(times):
-            dt = datetime.fromtimestamp(random.randint(15839593, 1294102139))
-            w.add_document(id=unicode(i), date=dt)
-        w.commit()
-        print "Writing took ", now() - t
-        
-        ix = index.open_dir(dirname)
-        s = ix.searcher()
-        q = query.Wildcard("id", "1?2*")
-        
-        t = now()
-        x = list(df.sortable_values(s.reader(), "date"))
-        print now() - t, len(x)
-        
-        t = now()
-        for y in x:
-            p = list(s.postings("date", y).all_ids())
-        print now() - t
-        
-        
-        
-        t = now()
-        r = s.search(q, limit=25, sortedby="date", reverse=True)
-        print "Search 1 took", now() - t
-        print "len=", r.scored_length()
-        
-        t = now()
-        r = s.search(q, limit=25, sortedby="date")
-        print "Search 2 took", now() - t
-        
-        t = now()
-        r = s.search(q, limit=25, sortedby="date")
-        print "Search 2 took", now() - t
-        
-        from heapq import nlargest
-        t = now()
-        sf = s.stored_fields
-        gen = ((sf(n)["date"], n) for n in q.docs(s))
-        r = nlargest(25, gen)
-        print now() - t
+def test_bigsort():
+    times = 30000
+    dirname = "testindex"
+    
+    df = fields.DATETIME(stored=True)
+    schema = fields.Schema(id=fields.ID(stored=True), date=df)
+    
+    if os.path.exists(dirname):
+        shutil.rmtree(dirname)
+    os.mkdir(dirname)
+    ix = index.create_in(dirname, schema)
+    
+    print "Writing..."
+    t = now()
+    w = ix.writer(limitmb=512)
+    for i in xrange(times):
+        dt = datetime.fromtimestamp(random.randint(15839593, 1294102139))
+        w.add_document(id=unicode(i), date=dt)
+    w.commit()
+    print "Writing took ", now() - t
+    
+    ix = index.open_dir(dirname)
+    s = ix.searcher()
+    q = query.Wildcard("id", "1?2*")
+    
+    t = now()
+    x = list(df.sortable_values(s.reader(), "date"))
+    print now() - t, len(x)
+    
+    t = now()
+    for y in x:
+        p = list(s.postings("date", y).all_ids())
+    print now() - t
+    
+    
+    
+    t = now()
+    r = s.search(q, limit=25, sortedby="date", reverse=True)
+    print "Search 1 took", now() - t
+    print "len=", r.scored_length()
+    
+    t = now()
+    r = s.search(q, limit=25, sortedby="date")
+    print "Search 2 took", now() - t
+    
+    t = now()
+    r = s.search(q, limit=25, sortedby="date")
+    print "Search 2 took", now() - t
+    
+    from heapq import nlargest
+    t = now()
+    sf = s.stored_fields
+    gen = ((sf(n)["date"], n) for n in q.docs(s))
+    r = nlargest(25, gen)
+    print now() - t
         
     
     

stress/test_bigtable.py

-import unittest
+from __future__ import with_statement
 
-import os.path, shutil, tempfile
 from random import randint, shuffle
 
-from whoosh.filedb.filestore import FileStorage
-from whoosh.filedb.filetables import HashWriter, HashReader, dump_hash
+from nose.tools import assert_equal
 
-class Test(unittest.TestCase):
-    def test_bigtable(self):
-        dir = tempfile.mkdtemp(prefix="bigtable", suffix=".tmpix")
-        st = FileStorage(dir)
-        
+from whoosh.filedb.filetables import HashWriter, HashReader
+from whoosh.support.testing import TempStorage
+
+
+def test_bigtable():
+    with TempStorage("bigtable") as st:
         def randstring(min, max):
             return "".join(chr(randint(1, 255))
                            for _ in xrange(randint(min, max)))
         keys = samp.keys()
         shuffle(keys)
         for key in keys:
-            self.assertEqual(samp[key], fhr[key])
+            assert_equal(samp[key], fhr[key])
         
         set1 = set(samp.iteritems())
         set2 = set(fhr.items())
-        self.assertEqual(set1, set2)
+        assert_equal(set1, set2)
         
         fhr.close()
-        shutil.rmtree(dir)
 
 
-if __name__ == "__main__":
-    #import sys;sys.argv = ['', 'Test.testName']
-    unittest.main()

stress/test_hugeindex.py

-import unittest
+from __future__ import with_statement
+import struct
 
-import shutil, struct, tempfile
+from nose.tools import assert_equal
 
 from whoosh import formats
-from whoosh.filedb.filestore import FileStorage
 from whoosh.filedb.filepostings import FilePostingReader, FilePostingWriter
-from whoosh.util import now
+from whoosh.support.testing import TempStorage
 
 
-class Test(unittest.TestCase):
-    def test_huge_postfile(self):
-        dir = tempfile.mkdtemp(prefix="hugeindex", suffix=".tmpix")
-        st = FileStorage(dir)
-        
+def test_huge_postfile():
+    with TempStorage("hugeindex") as st:
         pf = st.create_file("test.pst")
         
         gb5 = 5 * 1024 * 1024 * 1024
         pf.seek(gb5)
         pf.write("\x00\x00\x00\x00")
-        self.assertEqual(pf.tell(), gb5 + 4)
+        assert_equal(pf.tell(), gb5 + 4)
         
         fpw = FilePostingWriter(pf)
         format = formats.Frequency(None)
         for i in xrange(10):
             fpw.write(i, float(i), struct.pack("!I", i), 10)
         posttotal = fpw.finish()
-        self.assertEqual(posttotal, 10)
+        assert_equal(posttotal, 10)
         fpw.close()
         
         pf = st.open_file("test.pst")
         pfr = FilePostingReader(pf, offset, format)
         i = 0
         while pfr.is_active():
-            self.assertEqual(pfr.id(), i)
-            self.assertEqual(pfr.weight(), float(i))
-            self.assertEqual(pfr.value(), struct.pack("!I", i))
+            assert_equal(pfr.id(), i)
+            assert_equal(pfr.weight(), float(i))
+            assert_equal(pfr.value(), struct.pack("!I", i))
             pfr.next()
             i += 1
         pf.close()
-        
-        shutil.rmtree()
+    
         
         
     
 
 
 
-if __name__ == "__main__":
-    unittest.main()

stress/test_threading.py

-import unittest
+from __future__ import with_statement
+import random, threading, time
 
-import random, shutil, tempfile, threading, time
+from whoosh import fields, query
+from whoosh.support.testing import TempStorage
 
-from whoosh import fields, index, query
 
-
-class TestThreading(unittest.TestCase):
-    def test_readwrite(self):
-        dir = tempfile.mkdtemp(prefix="threading", suffix=".tmpix")
-        
-        ixname = "threading"
-        schema = fields.Schema(id=fields.ID(stored=True), content=fields.TEXT)
-        
+def test_readwrite():
+    schema = fields.Schema(id=fields.ID(stored=True), content=fields.TEXT)
+    with TempStorage("threading") as st:
         domain = ("alfa", "bravo", "charlie", "delta", "echo", "foxtrot",
                   "golf", "hotel", "india", "juliet", "kilo", "lima", "mike",
                   "november", "oscar", "papa", "quebec", "romeo", "sierra",
         
         class WriterThread(threading.Thread):
             def run(self):
-                ix = index.create_in(dir, schema, indexname=ixname)
+                ix = st.create_index(dir, schema)
                 num = 0
                 
                 for i in xrange(50):
             def run(self):
                 print self.name + " starting"
                 for _ in xrange(10):
-                    ix = index.open_dir(dir, indexname=ixname)
+                    ix = st.open_index()
                     s = ix.searcher()
                     q = query.Term("content", random.choice(domain))
                     s.search(q, limit=10)
             SearcherThread().start()
             time.sleep(0.5)
         wt.join()
-        
-        shutil.rmtree(dir)
+    
 
-if __name__ == '__main__':
-    unittest.main()

stress/test_update.py

 from __future__ import with_statement
-import unittest
 import random
 
+from nose.tools import assert_equal
+
 from whoosh import fields, query
 from whoosh.support.testing import TempIndex
 
 
-class Test(unittest.TestCase):
-    def test_many_updates(self):
-        schema = fields.Schema(key=fields.ID(unique=True, stored=True))
-        with TempIndex(schema, "manyupdates") as ix:
-            for _ in xrange(10000):
-                num = random.randint(0, 5000)
-                w = ix.writer()
-                w.update_document(key=unicode(num))
-                w.commit()
-            
-            with ix.searcher() as s:
-                result = [d["key"] for d in s.search(query.Every())]
-                self.assertEqual(len(result), len(set(result)))
+def test_many_updates():
+    schema = fields.Schema(key=fields.ID(unique=True, stored=True))
+    with TempIndex(schema, "manyupdates") as ix:
+        for _ in xrange(10000):
+            num = random.randint(0, 5000)
+            w = ix.writer()
+            w.update_document(key=unicode(num))
+            w.commit()
+        
+        with ix.searcher() as s:
+            result = [d["key"] for d in s.search(query.Every())]
+            assert_equal(len(result), len(set(result)))
 
                 
 
 
-if __name__ == "__main__":
-    unittest.main()

tests/__init__.py

Empty file removed.

tests/test_analysis.py

-import unittest
+from nose.tools import assert_equal
 
 from whoosh import fields, qparser
 from whoosh.analysis import *
 from whoosh.filedb.filestore import RamStorage
 
 
-class TestAnalysis(unittest.TestCase):
-    def test_regextokenizer(self):
-        value = u"AAAaaaBBBbbbCCCcccDDDddd"
+def test_regextokenizer():
+    value = u"AAAaaaBBBbbbCCCcccDDDddd"
+    
+    rex = RegexTokenizer("[A-Z]+")
+    assert_equal([t.text for t in rex(value)], [u"AAA", u"BBB", u"CCC", u"DDD"])
+    
+    rex = RegexTokenizer("[A-Z]+", gaps=True)
+    assert_equal([t.text for t in rex(value)], [u"aaa", u"bbb", u"ccc", u"ddd"])
+
+def test_composition1():
+    ca = RegexTokenizer() | LowercaseFilter()
+    assert_equal(ca.__class__.__name__, "CompositeAnalyzer")
+    assert_equal(ca[0].__class__.__name__, "RegexTokenizer")
+    assert_equal(ca[1].__class__.__name__, "LowercaseFilter")
+    assert_equal([t.text for t in ca(u"ABC 123")], ["abc", "123"])
+
+def test_composition2():
+    ca = RegexTokenizer() | LowercaseFilter()
+    sa = ca | StopFilter()
+    assert_equal(len(sa), 3)
+    assert_equal(sa.__class__.__name__, "CompositeAnalyzer")
+    assert_equal(sa[0].__class__.__name__, "RegexTokenizer")
+    assert_equal(sa[1].__class__.__name__, "LowercaseFilter")
+    assert_equal(sa[2].__class__.__name__, "StopFilter")
+    assert_equal([t.text for t in sa(u"The ABC 123")], ["abc", "123"])
+
+def test_composition3():
+    sa = RegexTokenizer() | StopFilter()
+    assert_equal(sa.__class__.__name__, "CompositeAnalyzer")
+
+def test_composing_functions():
+    def filter(tokens):
+        for t in tokens:
+            t.text = t.text.upper()
+            yield t
+            
+    analyzer = RegexTokenizer() | filter
+    assert_equal([t.text for t in analyzer(u"abc def")], ["ABC", "DEF"])
+
+def test_shared_composition():
+    shared = RegexTokenizer(r"\S+") | LowercaseFilter()
+    
+    ana1 = shared | NgramFilter(3)
+    ana2 = shared | DoubleMetaphoneFilter()
+    
+    assert_equal([t.text for t in ana1(u"hello")], ["hel", "ell", "llo"])
+    assert_equal([t.text for t in ana2(u"hello")], ["HL"])
+
+def test_multifilter():
+    f1 = LowercaseFilter()
+    f2 = PassFilter()
+    mf = MultiFilter(a=f1, b=f2)
+    ana = RegexTokenizer(r"\S+") | mf
+    text = u"ALFA BRAVO CHARLIE"
+    assert_equal([t.text for t in ana(text, mode="a")], ["alfa", "bravo", "charlie"])
+    assert_equal([t.text for t in ana(text, mode="b")], ["ALFA", "BRAVO", "CHARLIE"])
+
+def test_intraword():
+    iwf = IntraWordFilter(mergewords=True, mergenums=True)
+    ana = RegexTokenizer(r"\S+") | iwf
+    
+    def check(text, ls):
+        assert_equal([(t.pos, t.text) for t in ana(text)], ls)
         
-        rex = RegexTokenizer("[A-Z]+")
-        self.assertEqual([t.text for t in rex(value)],
-                         [u"AAA", u"BBB", u"CCC", u"DDD"])
-        
-        rex = RegexTokenizer("[A-Z]+", gaps=True)
-        self.assertEqual([t.text for t in rex(value)],
-                         [u"aaa", u"bbb", u"ccc", u"ddd"])
+    check(u"PowerShot", [(0, "Power"), (1, "Shot"), (1, "PowerShot")])
+    check(u"A's+B's&C's", [(0, "A"), (1, "B"), (2, "C"), (2, "ABC")])
+    check(u"Super-Duper-XL500-42-AutoCoder!",
+          [(0, "Super"), (1, "Duper"), (2, "XL"), (2, "SuperDuperXL"),
+           (3, "500"), (4, "42"), (4, "50042"), (5, "Auto"), (6, "Coder"),
+           (6, "AutoCoder")])
+
+def test_biword():
+    ana = RegexTokenizer(r"\w+") | BiWordFilter()
+    result = [t.copy() for t
+              in ana(u"the sign of four", chars=True, positions=True)]
+    assert_equal(["the-sign", "sign-of", "of-four"], [t.text for t in result])
+    assert_equal([(0, 8), (4, 11), (9, 16)], [(t.startchar, t.endchar) for t in result])
+    assert_equal([0, 1, 2], [t.pos for t in result])
     
-    def test_composition1(self):
-        ca = RegexTokenizer() | LowercaseFilter()
-        self.assertEqual(ca.__class__.__name__, "CompositeAnalyzer")
-        self.assertEqual(ca[0].__class__.__name__, "RegexTokenizer")
-        self.assertEqual(ca[1].__class__.__name__, "LowercaseFilter")
-        self.assertEqual([t.text for t in ca(u"ABC 123")], ["abc", "123"])
+    result = [t.copy() for t in ana(u"single")]
+    assert_equal(len(result), 1)
+    assert_equal(result[0].text, "single")
+
+def test_shingles():
+    ana = RegexTokenizer(r"\w+") | ShingleFilter(3, " ")
+    source = u"better a witty fool than a foolish wit"
+    results = [t.copy() for t in ana(source, positions=True, chars=True)]
+    assert_equal([t.text for t in results],
+                 [u'better a witty', u'a witty fool', u'witty fool than',
+                  u'fool than a', u'than a foolish', u'a foolish wit'])
+    assert_equal([t.pos for t in results], range(len(results)))
+    for t in results:
+        assert_equal(t.text, source[t.startchar:t.endchar])
     
-    def test_composition2(self):
-        ca = RegexTokenizer() | LowercaseFilter()
-        sa = ca | StopFilter()
-        self.assertEqual(len(sa), 3)
-        self.assertEqual(sa.__class__.__name__, "CompositeAnalyzer")
-        self.assertEqual(sa[0].__class__.__name__, "RegexTokenizer")
-        self.assertEqual(sa[1].__class__.__name__, "LowercaseFilter")
-        self.assertEqual(sa[2].__class__.__name__, "StopFilter")
-        self.assertEqual([t.text for t in sa(u"The ABC 123")], ["abc", "123"])
+def test_unicode_blocks():
+    from whoosh.support.unicode import blocks, blockname, blocknum
     
-    def test_composition3(self):
-        sa = RegexTokenizer() | StopFilter()
-        self.assertEqual(sa.__class__.__name__, "CompositeAnalyzer")
+    assert_equal(blockname(u'a'), 'Basic Latin')
+    assert_equal(blockname(unichr(0x0b80)), 'Tamil')
+    assert_equal(blockname(unichr(2048)), None)
+    assert_equal(blocknum(u'a'), 0)
+    assert_equal(blocknum(unichr(0x0b80)), 22)
+    assert_equal(blocknum(unichr(2048)), None)
+    assert_equal(blocknum(u'a'), blocks.Basic_Latin)
+    assert_equal(blocknum(unichr(0x0b80)), blocks.Tamil)
     
-    def test_composing_functions(self):
-        def filter(tokens):
-            for t in tokens:
-                t.text = t.text.upper()
-                yield t
-                
-        analyzer = RegexTokenizer() | filter
-        self.assertEqual([t.text for t in analyzer(u"abc def")], ["ABC", "DEF"])
+def test_double_metaphone():
+    mf = RegexTokenizer() | LowercaseFilter() | DoubleMetaphoneFilter()
+    results = [(t.text, t.boost) for t in mf(u"Spruce View")]
+    assert_equal(results, [('SPRS', 1.0), ('F', 1.0), ('FF', 0.5)])
     
-    def test_shared_composition(self):
-        shared = RegexTokenizer(r"\S+") | LowercaseFilter()
-        
-        ana1 = shared | NgramFilter(3)
-        ana2 = shared | DoubleMetaphoneFilter()
-        
-        self.assertEqual([t.text for t in ana1(u"hello")], ["hel", "ell", "llo"])
-        self.assertEqual([t.text for t in ana2(u"hello")], ["HL"])
+    mf = RegexTokenizer() | LowercaseFilter() | DoubleMetaphoneFilter(combine=True)
+    results = [(t.text, t.boost) for t in mf(u"Spruce View")]
+    assert_equal(results, [('spruce', 1.0), ('SPRS', 1.0), ('view', 1.0),
+                           ('F', 1.0), ('FF', 0.5)])
+
+    namefield = fields.TEXT(analyzer=mf)
+    texts = list(namefield.process_text(u"Spruce View", mode="query"))
+    assert_equal(texts, [u'spruce', 'SPRS', u'view', 'F', 'FF'])
+
+def test_substitution():
+    mf = RegexTokenizer(r"\S+") | SubstitutionFilter("-", "")
+    assert_equal([t.text for t in mf(u"one-two th-re-ee four")],
+                 ["onetwo", "threee", "four"])
     
-    def test_multifilter(self):
-        f1 = LowercaseFilter()
-        f2 = PassFilter()
-        mf = MultiFilter(a=f1, b=f2)
-        ana = RegexTokenizer(r"\S+") | mf
-        text = u"ALFA BRAVO CHARLIE"
-        self.assertEqual([t.text for t in ana(text, mode="a")], ["alfa", "bravo", "charlie"])
-        self.assertEqual([t.text for t in ana(text, mode="b")], ["ALFA", "BRAVO", "CHARLIE"])
+    mf = RegexTokenizer(r"\S+") | SubstitutionFilter("([^=]*)=(.*)", r"\2=\1")
+    assert_equal([t.text for t in mf(u"a=b c=d ef")], ["b=a", "d=c", "ef"])
+
+def test_delimited_attribute():
+    ana = RegexTokenizer(r"\S+") | DelimitedAttributeFilter()
+    results = [(t.text, t.boost) for t in ana(u"image render^2 file^0.5")]
+    assert_equal(results, [("image", 1.0), ("render", 2.0), ("file", 0.5)])
     
-    def test_intraword(self):
-        iwf = IntraWordFilter(mergewords=True, mergenums=True)
-        ana = RegexTokenizer(r"\S+") | iwf
-        
-        def check(text, ls):
-            self.assertEqual([(t.pos, t.text) for t in ana(text)], ls)
-            
-        check(u"PowerShot", [(0, "Power"), (1, "Shot"), (1, "PowerShot")])
-        check(u"A's+B's&C's", [(0, "A"), (1, "B"), (2, "C"), (2, "ABC")])
-        check(u"Super-Duper-XL500-42-AutoCoder!", [(0, "Super"), (1, "Duper"), (2, "XL"),
-                                                   (2, "SuperDuperXL"), (3, "500"), (4, "42"),
-                                                   (4, "50042"), (5, "Auto"), (6, "Coder"),
-                                                   (6, "AutoCoder")])
+def test_porter2():
+    from whoosh.lang.porter2 import stem
     
-    def test_biword(self):
-        ana = RegexTokenizer(r"\w+") | BiWordFilter()
-        result = [t.copy() for t in ana(u"the sign of four",
-                                        chars=True, positions=True)]
-        self.assertEqual(["the-sign", "sign-of", "of-four"],
-                         [t.text for t in result])
-        self.assertEqual([(0, 8), (4, 11), (9, 16)],
-                         [(t.startchar, t.endchar) for t in result])
-        self.assertEqual([0, 1, 2], [t.pos for t in result])
-        
-        result = [t.copy() for t in ana(u"single")]
-        self.assertEqual(len(result), 1)
-        self.assertEqual(result[0].text, "single")
+    plurals = ['caresses', 'flies', 'dies', 'mules', 'denied',
+               'died', 'agreed', 'owned', 'humbled', 'sized',
+               'meeting', 'stating', 'siezing', 'itemization',
+               'sensational', 'traditional', 'reference', 'colonizer',
+               'plotted']
+    singles = [stem(w) for w in plurals]
     
-    def test_shingles(self):
-        ana = RegexTokenizer(r"\w+") | ShingleFilter(3, " ")
-        source = u"better a witty fool than a foolish wit"
-        results = [t.copy() for t in ana(source, positions=True, chars=True)]
-        self.assertEqual([t.text for t in results],
-                         [u'better a witty', u'a witty fool',
-                          u'witty fool than', u'fool than a', u'than a foolish',
-                          u'a foolish wit'])
-        self.assertEqual([t.pos for t in results], range(len(results)))
-        for t in results:
-            self.assertEqual(t.text, source[t.startchar:t.endchar])
-        
-    def test_unicode_blocks(self):
-        from whoosh.support.unicode import blocks, blockname, blocknum
-        
-        self.assertEqual(blockname(u'a'), 'Basic Latin')
-        self.assertEqual(blockname(unichr(0x0b80)), 'Tamil')
-        self.assertEqual(blockname(unichr(2048)), None)
-        self.assertEqual(blocknum(u'a'), 0)
-        self.assertEqual(blocknum(unichr(0x0b80)), 22)
-        self.assertEqual(blocknum(unichr(2048)), None)
-        self.assertEqual(blocknum(u'a'), blocks.Basic_Latin)
-        self.assertEqual(blocknum(unichr(0x0b80)), blocks.Tamil)
-        
-    def test_double_metaphone(self):
-        mf = RegexTokenizer() | LowercaseFilter() | DoubleMetaphoneFilter()
-        results = [(t.text, t.boost) for t in mf(u"Spruce View")]
-        self.assertEqual(results, [('SPRS', 1.0), ('F', 1.0), ('FF', 0.5)])
-        
-        mf = RegexTokenizer() | LowercaseFilter() | DoubleMetaphoneFilter(combine=True)
-        results = [(t.text, t.boost) for t in mf(u"Spruce View")]
-        self.assertEqual(results, [('spruce', 1.0), ('SPRS', 1.0),
-                                   ('view', 1.0), ('F', 1.0), ('FF', 0.5)])
+    assert_equal(singles, ['caress', 'fli', 'die', 'mule', 'deni', 'die', 'agre',
+                           'own', 'humbl', 'size', 'meet', 'state', 'siez', 'item',
+                           'sensat', 'tradit', 'refer', 'colon', 'plot'])
+    assert_equal(stem("bill's"), "bill")
+    assert_equal(stem("y's"), "y")
+
+def test_url():
+    sample = u"Visit http://bitbucket.org/mchaput/whoosh or urn:isbn:5930502 or http://www.apple.com/."
     
-        namefield = fields.TEXT(analyzer=mf)
-        texts = list(namefield.process_text(u"Spruce View", mode="query"))
-        self.assertEqual(texts, [u'spruce', 'SPRS', u'view', 'F', 'FF'])
+    for ana in (SimpleAnalyzer(url_pattern),
+                StandardAnalyzer(url_pattern, stoplist=None)):
+        ts = [t.text for t in ana(sample)]
+        assert_equal(ts, [u'visit', u'http://bitbucket.org/mchaput/whoosh',
+                          u'or', u'urn:isbn:5930502', u'or', u'http://www.apple.com/'])
+
+def test_name_field():
+    ana = (RegexTokenizer(r"\S+")
+           | LowercaseFilter()
+           | DoubleMetaphoneFilter(combine=True))
+    namefield = fields.TEXT(analyzer=ana, multitoken_query="or")
+    schema = fields.Schema(id=fields.STORED, name=namefield)
     
-    def test_substitution(self):
-        mf = RegexTokenizer(r"\S+") | SubstitutionFilter("-", "")
-        self.assertEqual([t.text for t in mf(u"one-two th-re-ee four")], ["onetwo", "threee", "four"])
-        
-        mf = RegexTokenizer(r"\S+") | SubstitutionFilter("([^=]*)=(.*)", r"\2=\1")
-        self.assertEqual([t.text for t in mf(u"a=b c=d ef")], ["b=a", "d=c", "ef"])
+    ix = RamStorage().create_index(schema)
+    w = ix.writer()
+    w.add_document(id=u"one", name=u"Leif Ericson")
+    w.commit()
     
-    def test_delimited_attribute(self):
-        ana = RegexTokenizer(r"\S+") | DelimitedAttributeFilter()
-        results = [(t.text, t.boost) for t in ana(u"image render^2 file^0.5")]
-        self.assertEqual(results, [("image", 1.0), ("render", 2.0), ("file", 0.5)])
-        
-    def test_porter2(self):
-        from whoosh.lang.porter2 import stem
-        
-        plurals = ['caresses', 'flies', 'dies', 'mules', 'denied',
-                   'died', 'agreed', 'owned', 'humbled', 'sized',
-                   'meeting', 'stating', 'siezing', 'itemization',
-                   'sensational', 'traditional', 'reference', 'colonizer',
-                   'plotted']
-        singles = [stem(w) for w in plurals]
-        
-        self.assertEqual(singles, ['caress', 'fli', 'die', 'mule', 'deni',
-                                   'die', 'agre', 'own', 'humbl', 'size',
-                                   'meet', 'state', 'siez', 'item', 'sensat',
-                                   'tradit', 'refer', 'colon', 'plot'])
-        
-        self.assertEqual(stem("bill's"), "bill")
-        self.assertEqual(stem("y's"), "y")
-    
-    def test_url(self):
-        sample = u"Visit http://bitbucket.org/mchaput/whoosh or urn:isbn:5930502 or http://www.apple.com/."
-        
-        for ana in (SimpleAnalyzer(url_pattern),
-                    StandardAnalyzer(url_pattern, stoplist=None)):
-            ts = [t.text for t in ana(sample)]
-            self.assertEqual(ts, [u'visit',
-                                  u'http://bitbucket.org/mchaput/whoosh',
-                                  u'or', u'urn:isbn:5930502', u'or',
-                                  u'http://www.apple.com/'])
-    
-    def test_name_field(self):
-        ana = (RegexTokenizer(r"\S+")
-               | LowercaseFilter()
-               | DoubleMetaphoneFilter(combine=True))
-        namefield = fields.TEXT(analyzer=ana, multitoken_query="or")
-        schema = fields.Schema(id=fields.STORED, name=namefield)
-        
-        ix = RamStorage().create_index(schema)
-        w = ix.writer()
-        w.add_document(id=u"one", name=u"Leif Ericson")
-        w.commit()
-        
-        s = ix.searcher()
-        qp = qparser.QueryParser("name", schema)
-        q = qp.parse(u"leaf eriksen", normalize=False)
-        r = s.search(q)
-        self.assertEqual(len(r), 1)
+    s = ix.searcher()
+    qp = qparser.QueryParser("name", schema)
+    q = qp.parse(u"leaf eriksen", normalize=False)
+    r = s.search(q)
+    assert_equal(len(r), 1)
 
 
 
 
 
-if __name__ == '__main__':
-    unittest.main()

tests/test_classify.py

 from __future__ import with_statement
-import unittest
 
-import os.path
-from shutil import rmtree
+from nose.tools import assert_equal
 
-from whoosh import analysis, classify, fields, formats, index
+from whoosh import analysis, classify, fields, formats
 from whoosh.filedb.filestore import RamStorage
 
 
 
 text = u"How do I use a velocity field for particles"
 
-class TestClassify(unittest.TestCase):
-    def create_index(self):
-        analyzer = analysis.StandardAnalyzer()
-        vector_format = formats.Frequency(analyzer)
-        schema = fields.Schema(path=fields.ID(stored=True),
-                               content=fields.TEXT(analyzer=analyzer,
-                                                   vector=vector_format))
-        
-        ix = RamStorage().create_index(schema)
-        
-        w = ix.writer()
-        from string import ascii_lowercase
-        for letter, content in zip(ascii_lowercase, domain):
-            w.add_document(path=u"/%s" % letter, content=content)
-        w.commit()
-        
-        return ix
+def create_index():
+    analyzer = analysis.StandardAnalyzer()
+    vector_format = formats.Frequency(analyzer)
+    schema = fields.Schema(path=fields.ID(stored=True),
+                           content=fields.TEXT(analyzer=analyzer,
+                                               vector=vector_format))
     
-    def test_add_text(self):
-        ix = self.create_index()
-        
-        with ix.reader() as r:
-            exp = classify.Expander(r, "content")
-            exp.add_text(text)
-            self.assertEqual([t[0] for t in exp.expanded_terms(3)],
-                             ["particles", "velocity", "field"])
-        
-    def test_keyterms(self):
-        ix = self.create_index()
-        with ix.searcher() as s:
-            docnum = s.document_number(path="/a")
-            keys = list(s.key_terms([docnum], "content", numterms=3))
-            self.assertEqual([t[0] for t in keys],
-                             ["collision", "volume", "used"])
+    ix = RamStorage().create_index(schema)
     
-    def test_keyterms_from_text(self):
-        ix = self.create_index()
-        with ix.searcher() as s:
-            keys = list(s.key_terms_from_text("content", text))
-            self.assertEqual([t[0] for t in keys],
-                             ["particles", "velocity", "field"])
+    w = ix.writer()
+    from string import ascii_lowercase
+    for letter, content in zip(ascii_lowercase, domain):
+        w.add_document(path=u"/%s" % letter, content=content)
+    w.commit()
+    
+    return ix
+
+def test_add_text():
+    ix = create_index()
+    with ix.reader() as r:
+        exp = classify.Expander(r, "content")
+        exp.add_text(text)
+        assert_equal([t[0] for t in exp.expanded_terms(3)], ["particles", "velocity", "field"])
+    
+def test_keyterms():
+    ix = create_index()
+    with ix.searcher() as s:
+        docnum = s.document_number(path="/a")
+        keys = list(s.key_terms([docnum], "content", numterms=3))
+        assert_equal([t[0] for t in keys], ["collision", "volume", "used"])
+
+def test_keyterms_from_text():
+    ix = create_index()
+    with ix.searcher() as s:
+        keys = list(s.key_terms_from_text("content", text))
+        assert_equal([t[0] for t in keys], ["particles", "velocity", "field"])
         
 
 
 
-if __name__ == '__main__':
-    unittest.main()
 
 

tests/test_dateparse.py

-import unittest
 from datetime import datetime
 
+from nose.tools import assert_equal
+
 from whoosh.qparser.dateparse import *
 
 
 english = English()
 
 
-class TestDateParser(unittest.TestCase):
-    def assert_adatetime(self, at, **kwargs):
-        self.assertEqual(at.__class__, adatetime)
-        for key in adatetime.units:
-            val = getattr(at, key)
-            target = kwargs.get(key)
-            self.assertEqual(val, target, "at.%s=%r not %r in %r" % (key, val, target, kwargs))
+def assert_adatetime(at, **kwargs):
+    assert at.__class__ is adatetime
+    for key in adatetime.units:
+        val = getattr(at, key)
+        target = kwargs.get(key)
+        assert_equal(val, target, "at.%s=%r not %r in %r" % (key, val, target, kwargs))
+
+def assert_timespan(ts, sargs, eargs):
+    assert_adatetime(ts.start, **sargs)
+
+def assert_unamb(ts, **kwargs):
+    assert_unamb_span(ts, kwargs, kwargs)
+
+def assert_unamb_span(ts, sargs, eargs):
+    startdt = adatetime(**sargs).floor()
+    enddt = adatetime(**eargs).ceil()
+    assert_equal(ts.start, startdt, "start %s != %s" % (ts.start, startdt))
+    assert_equal(ts.end, enddt, "end %s != %s" % (ts.end, enddt))
+
+def assert_datespan(ts, startdate, enddate):
+    assert ts.__class__ is timespan
+    assert_equal(ts.start, startdate)
+    assert_equal(ts.end, enddate)
+
+#
+
+def test_simple(t=english.simple):
+    assert_adatetime(t.date_from("2005", basedate), year=2005)
+    assert_adatetime(t.date_from("200505", basedate), year=2005, month=5)
+    assert_adatetime(t.date_from("20050510", basedate), year=2005, month=5, day=10)
+    assert_adatetime(t.date_from("2005051001", basedate), year=2005, month=5, day=10, hour=1)
+    assert_adatetime(t.date_from("200505100108", basedate), year=2005, month=5, day=10, hour=1, minute=8)
+    assert_adatetime(t.date_from("20050510010835", basedate), year=2005, month=5, day=10, hour=1, minute=8, second=35)
     
-    def assert_timespan(self, ts, sargs, eargs):
-        self.assert_adatetime(ts.start, **sargs)
-        self.assert_adatetime(ts.end, **eargs)
+    assert_adatetime(t.date_from("2005-05", basedate), year=2005, month=5)
+    assert_adatetime(t.date_from("2005 05 10", basedate), year=2005, month=5, day=10)
+    assert_adatetime(t.date_from("2005.05.10.01", basedate), year=2005, month=5, day=10, hour=1)
+    assert_adatetime(t.date_from("2005/05/10 01:08", basedate), year=2005, month=5, day=10, hour=1, minute=8)
+    assert_adatetime(t.date_from("2005.05.10  01:08:35", basedate), year=2005, month=5, day=10, hour=1, minute=8, second=35)
     
-    def assert_unamb(self, ts, **kwargs):
-        self.assert_unamb_span(ts, kwargs, kwargs)
+    assert t.date_from("2005 02 31", basedate) is None
+    assert t.date_from("2005-13-32", basedate) is None
     
-    def assert_unamb_span(self, ts, sargs, eargs):
-        startdt = adatetime(**sargs).floor()
-        enddt = adatetime(**eargs).ceil()
-        self.assertEqual(ts.start, startdt, "start %s != %s" % (ts.start, startdt))
-        self.assertEqual(ts.end, enddt, "end %s != %s" % (ts.end, enddt))
+def test_time(t=english.time):
+    assert_adatetime(t.date_from("13:05", basedate), hour=13, minute=5)
+    assert t.date_from("28:91", basedate) is None
     
-    def assert_datespan(self, ts, startdate, enddate):
-        self.assertEqual(ts.__class__, timespan)
-        self.assertEqual(ts.start, startdate)
-        self.assertEqual(ts.end, enddate)
+    assert_adatetime(t.date_from("3pm", basedate), hour=15)
+    assert_adatetime(t.date_from("3 pm", basedate), hour=15)
+    assert_adatetime(t.date_from("10pm", basedate), hour=22)
+    assert_adatetime(t.date_from("10 pm", basedate), hour=22)
+    assert_adatetime(t.date_from("3am", basedate), hour=3)
+    assert_adatetime(t.date_from("3:15 am", basedate), hour=3, minute=15)
+    assert_adatetime(t.date_from("5:10pm", basedate), hour=17, minute=10)
+    assert_adatetime(t.date_from("12:45am", basedate), hour=0, minute=45)
+    assert_adatetime(t.date_from("12:45pm", basedate), hour=12, minute=45)
+    assert_adatetime(t.date_from("5:45:05 pm", basedate), hour=17, minute=45, second=5)
     
-    #
+    assert_adatetime(t.date_from("noon", basedate), hour=12, minute=0, second=0, microsecond=0)
+    assert_adatetime(t.date_from("midnight", basedate), hour=0, minute=0, second=0, microsecond=0)
     
-    def test_simple(self, t=english.simple):
-        self.assert_adatetime(t.date_from("2005", basedate), year=2005)
-        self.assert_adatetime(t.date_from("200505", basedate), year=2005, month=5)
-        self.assert_adatetime(t.date_from("20050510", basedate), year=2005, month=5, day=10)
-        self.assert_adatetime(t.date_from("2005051001", basedate), year=2005, month=5, day=10, hour=1)
-        self.assert_adatetime(t.date_from("200505100108", basedate), year=2005, month=5, day=10, hour=1, minute=8)
-        self.assert_adatetime(t.date_from("20050510010835", basedate), year=2005, month=5, day=10, hour=1, minute=8, second=35)
-        
-        self.assert_adatetime(t.date_from("2005-05", basedate), year=2005, month=5)
-        self.assert_adatetime(t.date_from("2005 05 10", basedate), year=2005, month=5, day=10)
-        self.assert_adatetime(t.date_from("2005.05.10.01", basedate), year=2005, month=5, day=10, hour=1)
-        self.assert_adatetime(t.date_from("2005/05/10 01:08", basedate), year=2005, month=5, day=10, hour=1, minute=8)
-        self.assert_adatetime(t.date_from("2005.05.10  01:08:35", basedate), year=2005, month=5, day=10, hour=1, minute=8, second=35)
-        
-        self.assertEqual(t.date_from("2005 02 31", basedate), None)
-        self.assertEqual(t.date_from("2005-13-32", basedate), None)
-        
-    def test_time(self, t=english.time):
-        self.assert_adatetime(t.date_from("13:05", basedate), hour=13, minute=5)
-        self.assertEqual(t.date_from("28:91", basedate), None)
-        
-        self.assert_adatetime(t.date_from("3pm", basedate), hour=15)
-        self.assert_adatetime(t.date_from("3 pm", basedate), hour=15)
-        self.assert_adatetime(t.date_from("10pm", basedate), hour=22)
-        self.assert_adatetime(t.date_from("10 pm", basedate), hour=22)
-        self.assert_adatetime(t.date_from("3am", basedate), hour=3)
-        self.assert_adatetime(t.date_from("3:15 am", basedate), hour=3, minute=15)
-        self.assert_adatetime(t.date_from("5:10pm", basedate), hour=17, minute=10)
-        self.assert_adatetime(t.date_from("12:45am", basedate), hour=0, minute=45)
-        self.assert_adatetime(t.date_from("12:45pm", basedate), hour=12, minute=45)
-        self.assert_adatetime(t.date_from("5:45:05 pm", basedate), hour=17, minute=45, second=5)
-        
-        self.assert_adatetime(t.date_from("noon", basedate), hour=12, minute=0, second=0, microsecond=0)
-        self.assert_adatetime(t.date_from("midnight", basedate), hour=0, minute=0, second=0, microsecond=0)
-        
-        self.assertEqual(t.date_from("15 am", basedate), None)
-        self.assertEqual(t.date_from("24:00", basedate), None)
-        self.assertEqual(t.date_from("12:65", basedate), None)
+    assert t.date_from("15 am", basedate) is None
+    assert t.date_from("24:00", basedate) is None
+    assert t.date_from("12:65", basedate) is None
+
+def test_dmy(d=english.dmy):
+    assert_adatetime(d.date_from("25 may 2011", basedate), year=2011, month=5, day=25)
+    assert_adatetime(d.date_from("may 2 2011", basedate), year=2011, month=5, day=2)
+    assert_adatetime(d.date_from("2011 25 may", basedate), year=2011, month=5, day=25)
+    assert_adatetime(d.date_from("2011 may 5", basedate), year=2011, month=5, day=5)
     
-    def test_dmy(self, d=english.dmy):
-        self.assert_adatetime(d.date_from("25 may 2011", basedate), year=2011, month=5, day=25)
-        self.assert_adatetime(d.date_from("may 2 2011", basedate), year=2011, month=5, day=2)
-        self.assert_adatetime(d.date_from("2011 25 may", basedate), year=2011, month=5, day=25)
-        self.assert_adatetime(d.date_from("2011 may 5", basedate), year=2011, month=5, day=5)
-        
-        self.assert_adatetime(d.date_from("apr", basedate), month=4)
-        self.assert_adatetime(d.date_from("september", basedate), month=9)
-        self.assert_adatetime(d.date_from("2001", basedate), year=2001)
-        self.assert_adatetime(d.date_from("july 2525", basedate), year=2525, month=7)
-        self.assert_adatetime(d.date_from("nov 30", basedate), month=11, day=30)
-        self.assertEqual(d.date_from("25 2525", basedate), None)
-        
-        self.assert_adatetime(d.date_from("25 may, 2011", basedate), year=2011, month=5, day=25)
-        self.assert_adatetime(d.date_from("may 2nd, 2011", basedate), year=2011, month=5, day=2)
-        self.assert_adatetime(d.date_from("2011, 25 may", basedate), year=2011, month=5, day=25)
-        self.assert_adatetime(d.date_from("2011, may 5th", basedate), year=2011, month=5, day=5)
-        
-        self.assert_adatetime(d.date_from("today", basedate), year=2010, month=9, day=20)
-        self.assert_adatetime(d.date_from("tomorrow", basedate), year=2010, month=9, day=21)
-        self.assert_adatetime(d.date_from("yesterday", basedate), year=2010, month=9, day=19)
-        self.assert_adatetime(d.date_from("this month", basedate), year=2010, month=9)
-        self.assert_adatetime(d.date_from("this year", basedate), year=2010)
-        
-        self.assertEqual(d.date_from("now", basedate), basedate)
-        
-    def test_plustime(self, rt=english.plusdate):
-        self.assertEqual(rt.date_from("+1hr", basedate),
-                         basedate + timedelta(hours=1))
-        self.assertEqual(rt.date_from("+5mins", basedate),
-                         basedate + timedelta(minutes=5))
-        self.assertEqual(rt.date_from("+20s", basedate),
-                         basedate + timedelta(seconds=20))
-        
-        self.assertEqual(rt.date_from("- 2 h", basedate),
-                         basedate + timedelta(hours=-2))
-        self.assertEqual(rt.date_from("- 25 minutes", basedate),
-                         basedate + timedelta(minutes=-25))
-        self.assertEqual(rt.date_from("-400 secs", basedate),
-                         basedate + timedelta(seconds=-400))
-        
-        self.assertEqual(rt.date_from("+1hr 5m", basedate),
-                         basedate + timedelta(hours=1, minutes=5))
-        self.assertEqual(rt.date_from("-8hr 12m", basedate),
-                         basedate + timedelta(hours=-8, minutes=-12))
-        self.assertEqual(rt.date_from("+1hr 5s", basedate),
-                         basedate + timedelta(hours=1, seconds=5))
-        self.assertEqual(rt.date_from("+1hr 12m 5s", basedate),
-                         basedate + timedelta(hours=1, minutes=12, seconds=5))
-        self.assertEqual(rt.date_from("-1hr 5s", basedate),
-                         basedate + timedelta(hours=-1, seconds=-5))
-        self.assertEqual(rt.date_from("-1hr 12m 5s", basedate),
-                         basedate + timedelta(hours=-1, minutes=-12, seconds=-5))
-        
-    def test_relative_days(self):
-        # "next monday" on monday
-        self.assertEqual(relative_days(0, 0, 1), 7)
-        # "last monday" on monday
-        self.assertEqual(relative_days(0, 0, -1), -7)
-        # "next tuesday" on wednesday
-        self.assertEqual(relative_days(2, 1, 1), 6)
-        # "last tuesday" on wednesay
-        self.assertEqual(relative_days(2, 1, -1), -1)
-        # "last monday" on sunday
-        self.assertEqual(relative_days(6, 0, -1), -6)
-        # "next monday" on sunday
-        self.assertEqual(relative_days(6, 0, 1), 1)
-        # "next wednesday" on tuesday
-        self.assertEqual(relative_days(1, 2, 1), 1)
-        # "last wednesday" on tuesday
-        self.assertEqual(relative_days(1, 2, -1), -6)
-        # "last wednesday" on thursday
-        self.assertEqual(relative_days(3, 2, -1), -1)
-        # "next wednesday" on thursday
-        self.assertEqual(relative_days(3, 2, 1), 6)
-        # "last wednesday" on tuesday
-        self.assertEqual(relative_days(1, 2, -1), -6)
-        # "next wednesday" on tuesday
-        self.assertEqual(relative_days(1, 2, 1), 1)
-        
-    def test_dayname(self, p=english.dayname):
-        self.assert_adatetime(p.date_from("next tuesday", basedate), year=2010, month=9, day=21)
-        self.assert_adatetime(p.date_from("last tuesday", basedate), year=2010, month=9, day=14)
-        self.assert_adatetime(p.date_from("next sunday", basedate), year=2010, month=9, day=26)
-        self.assert_adatetime(p.date_from("last sun", basedate), year=2010, month=9, day=19)
-        self.assert_adatetime(p.date_from("next th", basedate), year=2010, month=9, day=23)
-        
-    def test_reldate(self, p=english.plusdate):
-        self.assertEqual(p.date_from("+1y", basedate),
-                         basedate + relativedelta(years=1))
-        self.assertEqual(p.date_from("+2mo", basedate),
-                         basedate + relativedelta(months=2))
-        self.assertEqual(p.date_from("+3w", basedate),
-                         basedate + relativedelta(weeks=3))
-        self.assertEqual(p.date_from("+5d", basedate),
-                         basedate + relativedelta(days=5))
-        self.assertEqual(p.date_from("+5days", basedate),
-                         basedate + relativedelta(days=5))
-        
-        self.assertEqual(p.date_from("-6yr", basedate),
-                         basedate + relativedelta(years=-6))
-        self.assertEqual(p.date_from("- 7 mons", basedate),
-                         basedate + relativedelta(months=-7))
-        self.assertEqual(p.date_from("-8 wks", basedate),
-                         basedate + relativedelta(weeks=-8))
-        self.assertEqual(p.date_from("- 9 dy", basedate),
-                         basedate + relativedelta(days=-9))
-        
-        
-        self.assertEqual(p.date_from("+1y 12mo 400d", basedate),
-                         basedate + relativedelta(years=1, months=12, days=400))
-        self.assertEqual(p.date_from("-7mo 8d", basedate),
-                         basedate + relativedelta(months=-7, days=-8))
-        self.assertEqual(p.date_from("+5wks 2d", basedate),
-                         basedate + relativedelta(weeks=5, days=2))
-        self.assertEqual(p.date_from("-1y 1w", basedate),
-                         basedate + relativedelta(years=-1, weeks=-1))
-        
-        self.assertEqual(p.date_from("+1y 2d 5h 12s", basedate),
-                         basedate + relativedelta(years=1, days=2, hours=5, seconds=12))
-        
-    def test_bundle_subs(self, p=english.bundle):
-        self.test_time(p)
-        self.test_dmy(p)
-        self.test_plustime(p)
-        self.test_dayname(p)
-        self.test_reldate(p)
-        
-    def test_bundle(self, p=english.bundle):
-        self.assert_adatetime(p.date_from("mar 29 1972 2:45am", basedate),
-                          year=1972, month=3, day=29, hour=2, minute=45)
-        self.assert_adatetime(p.date_from("16:10:45 14 February 2005", basedate),
-                          year=2005, month=2, day=14, hour=16, minute=10, second=45)
-        self.assert_adatetime(p.date_from("1985 sept 12 12:01", basedate),
-                          year=1985, month=9, day=12, hour=12, minute=1)
-        self.assert_adatetime(p.date_from("5pm 21st oct 2005", basedate),
-                          year=2005, month=10, day=21, hour=17)
-        self.assert_adatetime(p.date_from("5:59:59pm next thur", basedate),
-                          year=2010, month=9, day=23, hour=17, minute=59, second=59)
+    assert_adatetime(d.date_from("apr", basedate), month=4)
+    assert_adatetime(d.date_from("september", basedate), month=9)
+    assert_adatetime(d.date_from("2001", basedate), year=2001)
+    assert_adatetime(d.date_from("july 2525", basedate), year=2525, month=7)
+    assert_adatetime(d.date_from("nov 30", basedate), month=11, day=30)
+    assert d.date_from("25 2525", basedate) is None
     
-    def test_ranges(self, p=english.torange):
-        self.assert_timespan(p.date_from("last tuesday to next tuesday", basedate),
-                             dict(year=2010, month=9, day=14),
-                             dict(year=2010, month=9, day=21))
-        self.assert_timespan(p.date_from("last monday to dec 25", basedate),
-                             dict(year=2010, month=9, day=13),
-                             dict(year=None, month=12, day=25))
-        self.assert_timespan(p.date_from("oct 25 to feb 14", basedate),
-                             dict(year=None, month=10, day=25),
-                             dict(year=None, month=2, day=14))
-        self.assert_timespan(p.date_from("3am oct 12 to 5pm", basedate),
-                             dict(year=None, month=10, day=12, hour=3),
-                             dict(year=None, month=None, day=None, hour=17))
-        self.assert_timespan(p.date_from("3am feb 12 to 5pm today", basedate),
-                             dict(year=None, month=2, day=12, hour=3),
-                             dict(year=2010, month=9, day=20, hour=17))
-        self.assert_timespan(p.date_from("feb to oct", basedate),
-                             dict(year=None, month=2),
-                             dict(year=None, month=10))
-        self.assert_timespan(p.date_from("oct 25 2005 11am to 5pm tomorrow", basedate),
-                             dict(year=2005, month=10, day=25, hour=11),
-                             dict(year=2010, month=9, day=21, hour=17))
-        self.assert_timespan(p.date_from("oct 5 2005 to november 20", basedate),
-                             dict(year=2005, month=10, day=5),
-                             dict(year=None, month=11, day=20))
-        self.assert_timespan(p.date_from("2007 to 2010", basedate),
-                             dict(year=2007, month=None, day=None),
-                             dict(year=2010, month=None, day=None))
-        self.assert_timespan(p.date_from("2007 to oct 12", basedate),
-                             dict(year=2007, month=None, day=None),
-                             dict(year=None, month=10, day=12))
-        
-        self.assert_datespan(p.date_from("-2d to +1w", basedate),
-                             basedate + relativedelta(days=-2),
-                             basedate + relativedelta(weeks=1))
+    assert_adatetime(d.date_from("25 may, 2011", basedate), year=2011, month=5, day=25)
+    assert_adatetime(d.date_from("may 2nd, 2011", basedate), year=2011, month=5, day=2)
+    assert_adatetime(d.date_from("2011, 25 may", basedate), year=2011, month=5, day=25)
+    assert_adatetime(d.date_from("2011, may 5th", basedate), year=2011, month=5, day=5)
     
-    def test_all(self):
-        p = english.all
-        self.test_bundle_subs(p)
-        self.test_bundle(p)
-        self.test_ranges(p)
+    assert_adatetime(d.date_from("today", basedate), year=2010, month=9, day=20)
+    assert_adatetime(d.date_from("tomorrow", basedate), year=2010, month=9, day=21)
+    assert_adatetime(d.date_from("yesterday", basedate), year=2010, month=9, day=19)
+    assert_adatetime(d.date_from("this month", basedate), year=2010, month=9)
+    assert_adatetime(d.date_from("this year", basedate), year=2010)
     
-    def test_final_dates(self, p=english):
-        self.assert_unamb(p.date_from("5:10pm", basedate),
-                          year=2010, month=9, day=20, hour=17, minute=10)
+    assert_equal(d.date_from("now", basedate), basedate)
     
-        self.assertEqual(p.date_from("may 32 2005", basedate), None)
-        self.assertEqual(p.date_from("2005 may 32", basedate), None)            
-        self.assertEqual(p.date_from("2005-13-32", basedate), None)
+def test_plustime(rt=english.plusdate):
+    assert_equal(rt.date_from("+1hr", basedate), basedate + timedelta(hours=1))
+    assert_equal(rt.date_from("+5mins", basedate), basedate + timedelta(minutes=5))
+    assert_equal(rt.date_from("+20s", basedate), basedate + timedelta(seconds=20))
     
-    def test_final_ranges(self, p=english):
-        self.assert_unamb_span(p.date_from("feb to nov", basedate),
-                               dict(year=2010, month=2),
-                               dict(year=2010, month=11))
-        
-        # 2005 to 10 oct 2009 -> jan 1 2005 to oct 31 2009
-        self.assert_unamb_span(p.date_from("2005 to 10 oct 2009", basedate),
-                               dict(year=2005),
-                               dict(year=2009, month=10, day=10))
-        
-        # jan 12 to oct 10 2009 -> jan 12 2009 to oct 10 2009
-        self.assert_unamb_span(p.date_from("jan 12 to oct 10 2009", basedate),
-                               dict(year=2009, month=1, day=12),
-                               dict(year=2009, month=10, day=10))
-        
-        # jan to oct 2009 -> jan 1 2009 to oct 31 2009
-        self.assert_unamb_span(p.date_from("jan to oct 2009", basedate),
-                               dict(year=2009, month=1),
-                               dict(year=2009, month=10, day=31))
-        
-        # mar 2005 to oct -> mar 1 2005 to oct 31 basedate.year
-        self.assert_unamb_span(p.date_from("mar 2005 to oct", basedate),
-                               dict(year=2005, month=3),
-                               dict(year=2010, month=10, day=31))
-        
-        # jan 10 to jan 25 -> jan 10 basedate.year to jan 25 basedate.year
-        self.assert_unamb_span(p.date_from("jan 10 to jan 25", basedate),
-                               dict(year=2010, month=1, day=10),
-                               dict(year=2010, month=1, day=25))
-        
-        # jan 2005 to feb 2009 -> jan 1 2005 to feb 28 2009
-        self.assert_unamb_span(p.date_from("jan 2005 to feb 2009", basedate),
-                               dict(year=2005, month=1),
-                               dict(year=2009, month=2))
-        
-        # jan 5000 to mar -> jan 1 5000 to mar 5000
-        self.assert_unamb_span(p.date_from("jan 5000 to mar", basedate),
-                               dict(year=5000, month=1),
-                               dict(year=5000, month=3))
-        
-        # jun 5000 to jan -> jun 1 5000 to jan 31 5001
-        self.assert_unamb_span(p.date_from("jun 5000 to jan", basedate),
-                               dict(year=5000, month=6),
-                               dict(year=5001, month=1))
-        
-        # oct 2010 to feb -> oct 1 2010 to feb 28 2011
-        self.assert_unamb_span(p.date_from("oct 2010 to feb"),
-                               dict(year=2010, month=10),
-                               dict(year=2011, month=2))
-        
-        self.assert_unamb_span(p.date_from("5pm to 3am", basedate),
-                               dict(year=2010, month=9, day=20, hour=17),
-                               dict(year=2010, month=9, day=21, hour=3))
-        
-        self.assert_unamb_span(p.date_from("5am to 3 am tomorrow", basedate),
-                               dict(year=2010, month=9, day=20, hour=5),
-                               dict(year=2010, month=9, day=21, hour=3))
-        
-        self.assert_unamb_span(p.date_from("3am to 5 pm tomorrow", basedate),
-                               dict(year=2010, month=9, day=21, hour=3),
-                               dict(year=2010, month=9, day=21, hour=17))
-        
-        self.assert_unamb_span(p.date_from("-2hrs to +20min", basedate),
-                               dict(year=2010, month=9, day=20, hour=13, minute=16, second=6, microsecond=454000),
-                               dict(year=2010, month=9, day=20, hour=15, minute=36, second=6, microsecond=454000))
-        
-        # Swap
-        self.assert_unamb_span(p.date_from("oct 25 2009 to feb 14 2008", basedate),
-                               dict(year=2008, month=2, day=14),
-                               dict(year=2009, month=10, day=25))
+    assert_equal(rt.date_from("- 2 h", basedate), basedate + timedelta(hours=-2))
+    assert_equal(rt.date_from("- 25 minutes", basedate), basedate + timedelta(minutes=-25))
+    assert_equal(rt.date_from("-400 secs", basedate), basedate + timedelta(seconds=-400))
     
-        self.assert_unamb_span(p.date_from("oct 25 5000 to tomorrow", basedate),
-                               dict(year=2010, month=9, day=21),
-                               dict(year=5000, month=10, day=25))
+    assert_equal(rt.date_from("+1hr 5m", basedate), basedate + timedelta(hours=1, minutes=5))
+    assert_equal(rt.date_from("-8hr 12m", basedate), basedate + timedelta(hours=-8, minutes=-12))
+    assert_equal(rt.date_from("+1hr 5s", basedate), basedate + timedelta(hours=1, seconds=5))
+    assert_equal(rt.date_from("+1hr 12m 5s", basedate), basedate + timedelta(hours=1, minutes=12, seconds=5))
+    assert_equal(rt.date_from("-1hr 5s", basedate), basedate + timedelta(hours=-1, seconds=-5))
+    assert_equal(rt.date_from("-1hr 12m 5s", basedate), basedate + timedelta(hours=-1, minutes=-12, seconds=-5))
+    
+def test_relative_days():
+    # "next monday" on monday
+    assert_equal(relative_days(0, 0, 1), 7)
+    # "last monday" on monday
+    assert_equal(relative_days(0, 0, -1), -7)
+    # "next tuesday" on wednesday
+    assert_equal(relative_days(2, 1, 1), 6)
+    # "last tuesday" on wednesay
+    assert_equal(relative_days(2, 1, -1), -1)
+    # "last monday" on sunday
+    assert_equal(relative_days(6, 0, -1), -6)
+    # "next monday" on sunday
+    assert_equal(relative_days(6, 0, 1), 1)
+    # "next wednesday" on tuesday
+    assert_equal(relative_days(1, 2, 1), 1)
+    # "last wednesday" on tuesday
+    assert_equal(relative_days(1, 2, -1), -6)
+    # "last wednesday" on thursday
+    assert_equal(relative_days(3, 2, -1), -1)
+    # "next wednesday" on thursday
+    assert_equal(relative_days(3, 2, 1), 6)
+    # "last wednesday" on tuesday
+    assert_equal(relative_days(1, 2, -1), -6)
+    # "next wednesday" on tuesday
+    assert_equal(relative_days(1, 2, 1), 1)
+    
+def test_dayname(p=english.dayname):
+    assert_adatetime(p.date_from("next tuesday", basedate), year=2010, month=9, day=21)
+    assert_adatetime(p.date_from("last tuesday", basedate), year=2010, month=9, day=14)
+    assert_adatetime(p.date_from("next sunday", basedate), year=2010, month=9, day=26)
+    assert_adatetime(p.date_from("last sun", basedate), year=2010, month=9, day=19)
+    assert_adatetime(p.date_from("next th", basedate), year=2010, month=9, day=23)
+    
+def test_reldate(p=english.plusdate):
+    assert_equal(p.date_from("+1y", basedate), basedate + relativedelta(years=1))
+    assert_equal(p.date_from("+2mo", basedate), basedate + relativedelta(months=2))
+    assert_equal(p.date_from("+3w", basedate), basedate + relativedelta(weeks=3))
+    assert_equal(p.date_from("+5d", basedate), basedate + relativedelta(days=5))
+    assert_equal(p.date_from("+5days", basedate), basedate + relativedelta(days=5))
+    
+    assert_equal(p.date_from("-6yr", basedate), basedate + relativedelta(years=-6))
+    assert_equal(p.date_from("- 7 mons", basedate), basedate + relativedelta(months=-7))
+    assert_equal(p.date_from("-8 wks", basedate), basedate + relativedelta(weeks=-8))
+    assert_equal(p.date_from("- 9 dy", basedate), basedate + relativedelta(days=-9))
+    
+    assert_equal(p.date_from("+1y 12mo 400d", basedate), basedate + relativedelta(years=1, months=12, days=400))
+    assert_equal(p.date_from("-7mo 8d", basedate), basedate + relativedelta(months=-7, days=-8))
+    assert_equal(p.date_from("+5wks 2d", basedate), basedate + relativedelta(weeks=5, days=2))
+    assert_equal(p.date_from("-1y 1w", basedate), basedate + relativedelta(years=-1, weeks=-1))
+    
+    assert_equal(p.date_from("+1y 2d 5h 12s", basedate), basedate + relativedelta(years=1, days=2, hours=5, seconds=12))
+    
+def test_bundle_subs(p=english.bundle):
+    test_time(p)
+    test_dmy(p)
+    test_plustime(p)
+    test_dayname(p)
+    test_reldate(p)
+    
+def test_bundle(p=english.bundle):
+    assert_adatetime(p.date_from("mar 29 1972 2:45am", basedate),
+                      year=1972, month=3, day=29, hour=2, minute=45)
+    assert_adatetime(p.date_from("16:10:45 14 February 2005", basedate),
+                      year=2005, month=2, day=14, hour=16, minute=10, second=45)
+    assert_adatetime(p.date_from("1985 sept 12 12:01", basedate),
+                      year=1985, month=9, day=12, hour=12, minute=1)
+    assert_adatetime(p.date_from("5pm 21st oct 2005", basedate),
+                      year=2005, month=10, day=21, hour=17)
+    assert_adatetime(p.date_from("5:59:59pm next thur", basedate),
+                      year=2010, month=9, day=23, hour=17, minute=59, second=59)
+
+def test_ranges(p=english.torange):
+    assert_timespan(p.date_from("last tuesday to next tuesday", basedate),
+                         dict(year=2010, month=9, day=14),
+                         dict(year=2010, month=9, day=21))
+    assert_timespan(p.date_from("last monday to dec 25", basedate),
+                         dict(year=2010, month=9, day=13),
+                         dict(year=None, month=12, day=25))
+    assert_timespan(p.date_from("oct 25 to feb 14", basedate),
+                         dict(year=None, month=10, day=25),
+                         dict(year=None, month=2, day=14))
+    assert_timespan(p.date_from("3am oct 12 to 5pm", basedate),
+                         dict(year=None, month=10, day=12, hour=3),
+                         dict(year=None, month=None, day=None, hour=17))
+    assert_timespan(p.date_from("3am feb 12 to 5pm today", basedate),
+                         dict(year=None, month=2, day=12, hour=3),
+                         dict(year=2010, month=9, day=20, hour=17))
+    assert_timespan(p.date_from("feb to oct", basedate),
+                         dict(year=None, month=2),
+                         dict(year=None, month=10))
+    assert_timespan(p.date_from("oct 25 2005 11am to 5pm tomorrow", basedate),
+                         dict(year=2005, month=10, day=25, hour=11),
+                         dict(year=2010, month=9, day=21, hour=17))
+    assert_timespan(p.date_from("oct 5 2005 to november 20", basedate),
+                         dict(year=2005, month=10, day=5),
+                         dict(year=None, month=11, day=20))
+    assert_timespan(p.date_from("2007 to 2010", basedate),
+                         dict(year=2007, month=None, day=None),
+                         dict(year=2010, month=None, day=None))
+    assert_timespan(p.date_from("2007 to oct 12", basedate),
+                         dict(year=2007, month=None, day=None),
+                         dict(year=None, month=10, day=12))
+    
+    assert_datespan(p.date_from("-2d to +1w", basedate),
+                         basedate + relativedelta(days=-2),
+                         basedate + relativedelta(weeks=1))
+
+def test_all():
+    p = english.all
+    test_bundle_subs(p)
+    test_bundle(p)
+    test_ranges(p)
+
+def test_final_dates(p=english):
+    assert_unamb(p.date_from("5:10pm", basedate),
+                      year=2010, month=9, day=20, hour=17, minute=10)
+
+    assert p.date_from("may 32 2005", basedate) is None
+    assert p.date_from("2005 may 32", basedate) is None        
+    assert p.date_from("2005-13-32", basedate) is None
+
+def test_final_ranges(p=english):
+    assert_unamb_span(p.date_from("feb to nov", basedate),
+                           dict(year=2010, month=2),
+                           dict(year=2010, month=11))
+    
+    # 2005 to 10 oct 2009 -> jan 1 2005 to oct 31 2009
+    assert_unamb_span(p.date_from("2005 to 10 oct 2009", basedate),
+                           dict(year=2005),
+                           dict(year=2009, month=10, day=10))
+    
+    # jan 12 to oct 10 2009 -> jan 12 2009 to oct 10 2009
+    assert_unamb_span(p.date_from("jan 12 to oct 10 2009", basedate),
+                           dict(year=2009, month=1, day=12),
+                           dict(year=2009, month=10, day=10))
+    
+    # jan to oct 2009 -> jan 1 2009 to oct 31 2009
+    assert_unamb_span(p.date_from("jan to oct 2009", basedate),
+                           dict(year=2009, month=1),
+                           dict(year=2009, month=10, day=31))
+    
+    # mar 2005 to oct -> mar 1 2005 to oct 31 basedate.year
+    assert_unamb_span(p.date_from("mar 2005 to oct", basedate),
+                           dict(year=2005, month=3),
+                           dict(year=2010, month=10, day=31))
+    
+    # jan 10 to jan 25 -> jan 10 basedate.year to jan 25 basedate.year
+    assert_unamb_span(p.date_from("jan 10 to jan 25", basedate),
+                           dict(year=2010, month=1, day=10),
+                           dict(year=2010, month=1, day=25))
+    
+    # jan 2005 to feb 2009 -> jan 1 2005 to feb 28 2009
+    assert_unamb_span(p.date_from("jan 2005 to feb 2009", basedate),
+                           dict(year=2005, month=1),
+                           dict(year=2009, month=2))
+    
+    # jan 5000 to mar -> jan 1 5000 to mar 5000
+    assert_unamb_span(p.date_from("jan 5000 to mar", basedate),
+                           dict(year=5000, month=1),
+                           dict(year=5000, month=3))
+    
+    # jun 5000 to jan -> jun 1 5000 to jan 31 5001
+    assert_unamb_span(p.date_from("jun 5000 to jan", basedate),
+                           dict(year=5000, month=6),
+                           dict(year=5001, month=1))
+    
+    # oct 2010 to feb -> oct 1 2010 to feb 28 2011
+    assert_unamb_span(p.date_from("oct 2010 to feb"),
+                           dict(year=2010, month=10),
+                           dict(year=2011, month=2))
+    
+    assert_unamb_span(p.date_from("5pm to 3am", basedate),
+                           dict(year=2010, month=9, day=20, hour=17),
+                           dict(year=2010, month=9, day=21, hour=3))
+    
+    assert_unamb_span(p.date_from("5am to 3 am tomorrow", basedate),
+                           dict(year=2010, month=9, day=20, hour=5),
+                           dict(year=2010, month=9, day=21, hour=3))
+    
+    assert_unamb_span(p.date_from("3am to 5 pm tomorrow", basedate),
+                           dict(year=2010, month=9, day=21, hour=3),
+                           dict(year=2010, month=9, day=21, hour=17))
+    
+    assert_unamb_span(p.date_from("-2hrs to +20min", basedate),
+                           dict(year=2010, month=9, day=20, hour=13, minute=16, second=6, microsecond=454000),
+                           dict(year=2010, month=9, day=20, hour=15, minute=36, second=6, microsecond=454000))
+    
+    # Swap
+    assert_unamb_span(p.date_from("oct 25 2009 to feb 14 2008", basedate),
+                           dict(year=2008, month=2, day=14),
+                           dict(year=2009, month=10, day=25))
+
+    assert_unamb_span(p.date_from("oct 25 5000 to tomorrow", basedate),
+                           dict(year=2010, month=9, day=21),
+                           dict(year=5000, month=10, day=25))
         
         
         
 
 
-if __name__ == '__main__':
-    unittest.main()
 
 
 

tests/test_fields.py

 from __future__ import with_statement
-import unittest
 from datetime import datetime, timedelta
 
+from nose.tools import assert_equal, assert_not_equal, assert_raises
+
 from whoosh import analysis, fields, qparser, query
 from whoosh.filedb.filestore import RamStorage
 from whoosh.support import numeric, times
 
 
-class TestSchema(unittest.TestCase):
-    def test_schema_eq(self):
-        a = fields.Schema()
-        b = fields.Schema()
-        self.assertEqual(a, b)
+def test_schema_eq():
+    a = fields.Schema()
+    b = fields.Schema()
+    assert_equal(a, b)
 
-        a = fields.Schema(id=fields.ID)
-        b = a.copy()
-        self.assertEqual(a["id"], b["id"])
-        self.assertEqual(a, b)
+    a = fields.Schema(id=fields.ID)
+    b = a.copy()
+    assert_equal(a["id"], b["id"])
+    assert_equal(a, b)
 
-        c = fields.Schema(id=fields.TEXT)
-        self.assertNotEqual(a, c)
+    c = fields.Schema(id=fields.TEXT)
+    assert_not_equal(a, c)
+
+def test_creation1():
+    s = fields.Schema()
+    s.add("content", fields.TEXT(phrase = True))
+    s.add("title", fields.TEXT(stored = True))
+    s.add("path", fields.ID(stored = True))
+    s.add("tags", fields.KEYWORD(stored = True))
+    s.add("quick", fields.NGRAM)
+    s.add("note", fields.STORED)
     
-    def test_creation1(self):
-        s = fields.Schema()
-        s.add("content", fields.TEXT(phrase = True))
-        s.add("title", fields.TEXT(stored = True))
-        s.add("path", fields.ID(stored = True))
-        s.add("tags", fields.KEYWORD(stored = True))
-        s.add("quick", fields.NGRAM)
-        s.add("note", fields.STORED)
+    assert_equal(s.names(), ["content", "note", "path", "quick", "tags", "title"])
+    assert "content" in s
+    assert "buzz" not in s
+    assert isinstance(s["tags"], fields.KEYWORD)
+
+def test_creation2():
+    s = fields.Schema(a=fields.ID(stored=True),
+                      b=fields.ID,
+                      c=fields.KEYWORD(scorable=True))
+    
+    assert_equal(s.names(), ["a", "b", "c"])
+    assert "a" in s
+    assert "b" in s
+    assert "c" in s
+
+def test_declarative():
+    class MySchema(fields.SchemaClass):
+        content = fields.TEXT
+        title = fields.TEXT
+        path = fields.ID
+        date = fields.DATETIME
+    
+    ix = RamStorage().create_index(MySchema)
+    assert_equal(ix.schema.names(), ["content", "date", "path", "title"])
+    
+    ix = RamStorage().create_index(MySchema())
+    assert_equal(ix.schema.names(), ["content", "date", "path", "title"])
+    
+    assert_raises(fields.FieldConfigurationError, RamStorage().create_index, object())
+
+def test_declarative_inherit():
+    class Parent(fields.SchemaClass):
+        path = fields.ID
+        date = fields.DATETIME
         
-        self.assertEqual(s.names(), ["content", "note", "path", "quick", "tags", "title"])
-        self.assert_("content" in s)
-        self.assertFalse("buzz" in s)
-        self.assert_(isinstance(s["tags"], fields.KEYWORD))
+    class Child(Parent):
+        content = fields.TEXT
         
-    def test_creation2(self):
-        s = fields.Schema(a=fields.ID(stored=True),
-                          b=fields.ID,
-                          c=fields.KEYWORD(scorable=True))
+    class Grandchild(Child):
+        title = fields.TEXT
         
-        self.assertEqual(s.names(), ["a", "b", "c"])
-        self.assertTrue("a" in s)
-        self.assertTrue("b" in s)
-        self.assertTrue("c" in s)
+    s = Grandchild()
+    assert_equal(s.names(), ["content", "date", "path", "title"])
+
+def test_badnames():
+    s = fields.Schema()
+    assert_raises(fields.FieldConfigurationError, s.add, "_test", fields.ID)
+    assert_raises(fields.FieldConfigurationError, s.add, "a f", fields.ID)
+
+def test_numeric_support():
+    intf = fields.NUMERIC(int, shift_step=0)
+    longf = fields.NUMERIC(long, shift_step=0)
+    floatf = fields.NUMERIC(float, shift_step=0)
     
-    def test_declarative(self):
-        class MySchema(fields.SchemaClass):
-            content = fields.TEXT
-            title = fields.TEXT
-            path = fields.ID
-            date = fields.DATETIME
+    def roundtrip(obj, num):
+        assert_equal(obj.from_text(obj.to_text(num)), num)
+    
+    roundtrip(intf, 0)
+    roundtrip(intf, 12345)
+    roundtrip(intf, -12345)
+    roundtrip(longf, 0)
+    roundtrip(longf, 85020450482)
+    roundtrip(longf, -85020450482)
+    roundtrip(floatf, 0)
+    roundtrip(floatf, 582.592)
+    roundtrip(floatf, -582.592)
+    roundtrip(floatf, -99.42)
+    
+    from random import shuffle
+    def roundtrip_sort(obj, start, end, step):
+        count = start
+        rng = []
+        while count < end:
+            rng.append(count)
+            count += step
         
-        ix = RamStorage().create_index(MySchema)
-        self.assertEqual(ix.schema.names(), ["content", "date", "path", "title"])
+        scrabled = rng[:]
+        shuffle(scrabled)
+        round = [obj.from_text(t) for t
+                 in sorted([obj.to_text(n) for n in scrabled])]
+        assert_equal(round, rng)
+    
+    roundtrip_sort(intf, -100, 100, 1)
+    roundtrip_sort(longf, -58902, 58249, 43)
+    roundtrip_sort(floatf, -99.42, 99.83, 2.38)
+
+def test_numeric():
+    schema = fields.Schema(id=fields.ID(stored=True),
+                           integer=fields.NUMERIC(int),
+                           floating=fields.NUMERIC(float))
+    ix = RamStorage().create_index(schema)
+    
+    w = ix.writer()
+    w.add_document(id=u"a", integer=5820, floating=1.2)
+    w.add_document(id=u"b", integer=22, floating=2.3)
+    w.add_document(id=u"c", integer=78, floating=3.4)
+    w.add_document(id=u"d", integer=13, floating=4.5)
+    w.add_document(id=u"e", integer=9, floating=5.6)
+    w.commit()
+    
+    with ix.searcher() as s:
+        qp = qparser.QueryParser("integer", schema)
         
-        ix = RamStorage().create_index(MySchema())
-        self.assertEqual(ix.schema.names(), ["content", "date", "path", "title"])
+        r = s.search(qp.parse("5820"))
+        assert_equal(len(r), 1)
+        assert_equal(r[0]["id"], "a")
+    
+    with ix.searcher() as s:
+        r = s.search(qp.parse("floating:4.5"))
+        assert_equal(len(r), 1)
+        assert_equal(r[0]["id"], "d")
+    
+    q = qp.parse("integer:*")
+    assert_equal(q.__class__, query.Every)
+    assert_equal(q.fieldname, "integer")
+    
+    q = qp.parse("integer:5?6")
+    assert_equal(q, query.NullQuery)
+    
+def test_decimal_numeric():
+    from decimal import Decimal
+    
+    f = fields.NUMERIC(int, decimal_places=4)
+    schema = fields.Schema(id=fields.ID(stored=True), deci=f)
+    ix = RamStorage().create_index(schema)
+    
+    assert_equal(f.from_text(f.to_text(Decimal("123.56"))), Decimal("123.56"))
+    
+    w = ix.writer()
+    w.add_document(id=u"a", deci=Decimal("123.56"))
+    w.add_document(id=u"b", deci=Decimal("0.536255"))
+    w.add_document(id=u"c", deci=Decimal("2.5255"))
+    w.add_document(id=u"d", deci=Decimal("58"))
+    w.commit()
+    
+    with ix.searcher() as s:
+        qp = qparser.QueryParser("deci", schema)
         
-        self.assertRaises(fields.FieldConfigurationError, RamStorage().create_index, object())
+        r = s.search(qp.parse("123.56"))
+        assert_equal(r[0]["id"], "a")
+        
+        r = s.search(qp.parse("0.536255"))
+        assert_equal(r[0]["id"], "b")
+
+def test_numeric_parsing():
+    schema = fields.Schema(id=fields.ID(stored=True), number=fields.NUMERIC)
     
-    def test_declarative_inherit(self):
-        class Parent(fields.SchemaClass):
-            path = fields.ID
-            date = fields.DATETIME
+    qp = qparser.QueryParser("number", schema)
+    q = qp.parse("[10 to *]")
+    assert_equal(q, query.NullQuery)
+    
+    q = qp.parse("[to 400]")
+    assert q.__class__ is query.NumericRange
+    assert_equal(q.start, None)
+    assert_equal(q.end, 400)
+    
+    q = qp.parse("[10 to]")
+    assert q.__class__ is query.NumericRange
+    assert_equal(q.start, 10)
+    assert_equal(q.end, None)
+    
+    q = qp.parse("[10 to 400]")
+    assert q.__class__ is query.NumericRange
+    assert_equal(q.start, 10)
+    assert_equal(q.end, 400)
+    
+def test_numeric_ranges():
+    schema = fields.Schema(id=fields.STORED, num=fields.NUMERIC)
+    ix = RamStorage().create_index(schema)
+    w = ix.writer()
+    
+    for i in xrange(400):
+        w.add_document(id=i, num=i)
+    w.commit()
+    
+    with ix.searcher() as s:
+        qp = qparser.QueryParser("num", schema)
+        
+        def check(qs, target):
+            q = qp.parse(qs)
+            result = [s.stored_fields(d)["id"] for d in q.docs(s)]
+            assert_equal(result, target)
+        
+        # Note that range() is always inclusive-exclusive
+        check("[10 to 390]", range(10, 390+1))
+        check("[100 to]", range(100, 400))
+        check("[to 350]", range(0, 350+1))
+        check("[16 to 255]", range(16, 255+1))
+        check("{10 to 390]", range(11, 390+1))
+        check("[10 to 390}", range(10, 390))
+        check("{10 to 390}", range(11, 390))
+        check("{16 to 255}", range(17, 255))
+    
+def test_decimal_ranges():
+    from decimal import Decimal
+    
+    schema = fields.Schema(id=fields.STORED, num=fields.NUMERIC(int, decimal_places=2))
+    ix = RamStorage().create_index(schema)
+    w = ix.writer()
+    count = Decimal("0.0")
+    inc = Decimal("0.2")
+    for _ in xrange(500):
+        w.add_document(id=str(count), num=count)
+        count += inc
+    w.commit()
+    
+    with ix.searcher() as s:
+        qp = qparser.QueryParser("num", schema)
+        
+        def check(qs, start, end):
+            q = qp.parse(qs)
+            result = [s.stored_fields(d)["id"] for d in q.docs(s)]
             
-        class Child(Parent):
-            content = fields.TEXT
+            target = []
+            count = Decimal(start)
+            limit = Decimal(end)
+            while count <= limit:
+                target.append(str(count))
+                count += inc
             
-        class Grandchild(Child):
-            title = fields.TEXT
-            
-        s = Grandchild()
-        self.assertEqual(s.names(), ["content", "date", "path", "title"])
+            assert_equal(result, target)
+        
+        check("[10.2 to 80.8]", "10.2", "80.8")
+        check("{10.2 to 80.8]", "10.4", "80.8")
+        check("[10.2 to 80.8}", "10.2", "80.6")
+        check("{10.2 to 80.8}", "10.4", "80.6")
+
+def test_nontext_document():
+    schema = fields.Schema(id=fields.STORED, num=fields.NUMERIC,
+                           date=fields.DATETIME, even=fields.BOOLEAN)
+    ix = RamStorage().create_index(schema)
     
-    def test_badnames(self):
-        s = fields.Schema()
-        self.assertRaises(fields.FieldConfigurationError, s.add, "_test", fields.ID)
-        self.assertRaises(fields.FieldConfigurationError, s.add, "a f", fields.ID)
+    dt = datetime.now()
+    w = ix.writer()
+    for i in xrange(50):
+        w.add_document(id=i, num=i, date=dt + timedelta(days=i), even=not(i % 2))
+    w.commit()
     
-    def test_numeric_support(self):
-        intf = fields.NUMERIC(int, shift_step=0)
-        longf = fields.NUMERIC(long, shift_step=0)
-        floatf = fields.NUMERIC(float, shift_step=0)
+    with ix.searcher() as s:
+        def check(kwargs, target):
+            result = [d['id'] for d in s.documents(**kwargs)]
+            assert_equal(result, target)
         
-        def roundtrip(obj, num):
-            self.assertEqual(obj.from_text(obj.to_text(num)), num)
+        check({"num": 49}, [49])
+        check({"date": dt + timedelta(days=30)}, [30])
+        check({"even": True}, range(0, 50, 2))
+
+def test_nontext_update():
+    schema = fields.Schema(id=fields.STORED, num=fields.NUMERIC(unique=True),
+                           date=fields.DATETIME(unique=True))
+    ix = RamStorage().create_index(schema)
+    
+    dt = datetime.now()
+    w = ix.writer()
+    for i in xrange(10):
+        w.add_document(id=i, num=i, date=dt + timedelta(days=i))
+    w.commit()
+    
+    w = ix.writer()
+    w.update_document(num=8, id="a")
+    w.update_document(num=2, id="b")
+    w.update_document(num=4, id="c")
+    w.update_document(date=dt + timedelta(days=5), id="d")
+    w.update_document(date=dt + timedelta(days=1), id="e")
+    w.update_document(date=dt + timedelta(days=7), id="f")
+    w.commit()
+    
+def test_datetime():
+    dtf = fields.DATETIME(stored=True)
+    schema = fields.Schema(id=fields.ID(stored=True), date=dtf)
+    st = RamStorage()
+    ix = st.create_index(schema)
+    
+    w = ix.writer()
+    for month in xrange(1, 12):
+        for day in xrange(1, 28):
+            w.add_document(id=u"%s-%s" % (month, day),
+                           date=datetime(2010, month, day, 14, 00, 00))
+    w.commit()
+    
+    with ix.searcher() as s:
+        qp = qparser.QueryParser("id", schema)
         
-        roundtrip(intf, 0)
-        roundtrip(intf, 12345)
-        roundtrip(intf, -12345)
-        roundtrip(longf, 0)
-        roundtrip(longf, 85020450482)
-        roundtrip(longf, -85020450482)
-        roundtrip(floatf, 0)
-        roundtrip(floatf, 582.592)
-        roundtrip(floatf, -582.592)
-        roundtrip(floatf, -99.42)
+        r = s.search(qp.parse("date:20100523"))
+        assert_equal(len(r), 1)
+        assert_equal(r[0]["id"], "5-23")
+        assert r[0]["date"].__class__ is datetime
+        assert_equal(r[0]["date"].month, 5)
+        assert_equal(r[0]["date"].day, 23)