mhs-xapian 1.0.18a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/AUTHORS +1 -0
- data/COPYING +340 -0
- data/ChangeLog +5876 -0
- data/HACKING +101 -0
- data/INSTALL +293 -0
- data/Makefile +722 -0
- data/Makefile.am +26 -0
- data/Makefile.in +722 -0
- data/NEWS +2110 -0
- data/README +59 -0
- data/Rakefile +51 -0
- data/TODO +47 -0
- data/aclocal.m4 +7675 -0
- data/config.guess +1501 -0
- data/config.h +56 -0
- data/config.h.in +55 -0
- data/config.status +1298 -0
- data/config.sub +1705 -0
- data/configure +18536 -0
- data/configure.ac +944 -0
- data/csharp/.deps/xapian_wrap.Plo +1 -0
- data/csharp/AssemblyInfo.cs +40 -0
- data/csharp/AssemblyInfo.cs.in +40 -0
- data/csharp/Auto.cs +46 -0
- data/csharp/BM25Weight.cs +107 -0
- data/csharp/BoolWeight.cs +103 -0
- data/csharp/Database.cs +275 -0
- data/csharp/DateValueRangeProcessor.cs +61 -0
- data/csharp/Document.cs +177 -0
- data/csharp/ESet.cs +94 -0
- data/csharp/ESetIterator.cs +117 -0
- data/csharp/Enquire.cs +274 -0
- data/csharp/ExpandDecider.cs +76 -0
- data/csharp/Flint.cs +58 -0
- data/csharp/InMemory.cs +46 -0
- data/csharp/MSet.cs +193 -0
- data/csharp/MSetIterator.cs +141 -0
- data/csharp/Makefile +868 -0
- data/csharp/Makefile.am +106 -0
- data/csharp/Makefile.in +868 -0
- data/csharp/MatchDecider.cs +76 -0
- data/csharp/MultiValueSorter.cs +63 -0
- data/csharp/NumberValueRangeProcessor.cs +61 -0
- data/csharp/PositionIterator.cs +101 -0
- data/csharp/PostingIterator.cs +125 -0
- data/csharp/Quartz.cs +58 -0
- data/csharp/Query.cs +150 -0
- data/csharp/QueryParser.cs +174 -0
- data/csharp/RSet.cs +102 -0
- data/csharp/Remote.cs +100 -0
- data/csharp/SWIGTYPE_p_std__string.cs +30 -0
- data/csharp/SWIGTYPE_p_std__vectorTXapian__Query_t.cs +30 -0
- data/csharp/SWIGTYPE_p_std__vectorTstd__string_t.cs +30 -0
- data/csharp/SimpleStopper.cs +64 -0
- data/csharp/SmokeTest.cs +178 -0
- data/csharp/Sorter.cs +76 -0
- data/csharp/Stem.cs +66 -0
- data/csharp/Stopper.cs +91 -0
- data/csharp/StringValueRangeProcessor.cs +53 -0
- data/csharp/TermGenerator.cs +152 -0
- data/csharp/TermIterator.cs +125 -0
- data/csharp/TradWeight.cs +107 -0
- data/csharp/ValueIterator.cs +102 -0
- data/csharp/ValueRangeProcessor.cs +76 -0
- data/csharp/Version.cs +60 -0
- data/csharp/Weight.cs +93 -0
- data/csharp/WritableDatabase.cs +153 -0
- data/csharp/Xapian.cs +65 -0
- data/csharp/XapianPINVOKE.cs +1527 -0
- data/csharp/docs/Makefile +450 -0
- data/csharp/docs/Makefile.am +16 -0
- data/csharp/docs/Makefile.in +450 -0
- data/csharp/docs/examples/SimpleExpand.cs +109 -0
- data/csharp/docs/examples/SimpleIndex.cs +71 -0
- data/csharp/docs/examples/SimpleSearch.cs +78 -0
- data/csharp/docs/index.html +211 -0
- data/csharp/util.i +233 -0
- data/csharp/xapian_wrap.cc +10338 -0
- data/csharp/xapian_wrap.h +93 -0
- data/depcomp +632 -0
- data/extconf.rb +20 -0
- data/generic/except.i +80 -0
- data/generic/generic.mk +48 -0
- data/install-sh +520 -0
- data/java-swig/.deps/xapian_wrap.Plo +1 -0
- data/java-swig/Auto.java +35 -0
- data/java-swig/BM25Weight.java +81 -0
- data/java-swig/BoolWeight.java +77 -0
- data/java-swig/Database.java +195 -0
- data/java-swig/DateValueRangeProcessor.java +51 -0
- data/java-swig/Document.java +135 -0
- data/java-swig/ESet.java +71 -0
- data/java-swig/ESetIterator.java +71 -0
- data/java-swig/Enquire.java +246 -0
- data/java-swig/ExpandDecider.java +59 -0
- data/java-swig/Flint.java +43 -0
- data/java-swig/InMemory.java +35 -0
- data/java-swig/MSet.java +143 -0
- data/java-swig/MSetIterator.java +87 -0
- data/java-swig/Makefile +781 -0
- data/java-swig/Makefile.am +132 -0
- data/java-swig/Makefile.in +781 -0
- data/java-swig/MatchDecider.java +59 -0
- data/java-swig/MultiValueSorter.java +51 -0
- data/java-swig/NumberValueRangeProcessor.java +51 -0
- data/java-swig/PositionIterator.java +63 -0
- data/java-swig/PostingIterator.java +83 -0
- data/java-swig/Quartz.java +43 -0
- data/java-swig/Query.java +189 -0
- data/java-swig/QueryParser.java +214 -0
- data/java-swig/RSet.java +79 -0
- data/java-swig/Remote.java +71 -0
- data/java-swig/SWIGTYPE_p_std__string.java +25 -0
- data/java-swig/SimpleStopper.java +51 -0
- data/java-swig/SmokeTest.java +161 -0
- data/java-swig/Sorter.java +59 -0
- data/java-swig/Stem.java +51 -0
- data/java-swig/Stopper.java +63 -0
- data/java-swig/StringValueRangeProcessor.java +43 -0
- data/java-swig/TermGenerator.java +158 -0
- data/java-swig/TermIterator.java +83 -0
- data/java-swig/TradWeight.java +81 -0
- data/java-swig/ValueIterator.java +67 -0
- data/java-swig/ValueRangeProcessor.java +59 -0
- data/java-swig/Version.java +47 -0
- data/java-swig/Weight.java +68 -0
- data/java-swig/WritableDatabase.java +123 -0
- data/java-swig/Xapian.java +39 -0
- data/java-swig/XapianConstants.java +15 -0
- data/java-swig/XapianJNI.java +508 -0
- data/java-swig/run-java-test +6 -0
- data/java-swig/xapian_wrap.cc +12594 -0
- data/java-swig/xapian_wrap.h +91 -0
- data/java/Makefile +660 -0
- data/java/Makefile.am +35 -0
- data/java/Makefile.in +660 -0
- data/java/README +76 -0
- data/java/SmokeTest.java +148 -0
- data/java/native/.deps/Database.Plo +1 -0
- data/java/native/.deps/Document.Plo +1 -0
- data/java/native/.deps/ESet.Plo +1 -0
- data/java/native/.deps/ESetIterator.Plo +1 -0
- data/java/native/.deps/Enquire.Plo +1 -0
- data/java/native/.deps/MSet.Plo +1 -0
- data/java/native/.deps/MSetIterator.Plo +1 -0
- data/java/native/.deps/PositionIterator.Plo +1 -0
- data/java/native/.deps/Query.Plo +1 -0
- data/java/native/.deps/RSet.Plo +1 -0
- data/java/native/.deps/Stem.Plo +1 -0
- data/java/native/.deps/TermIterator.Plo +1 -0
- data/java/native/.deps/WritableDatabase.Plo +1 -0
- data/java/native/.deps/org_xapian_XapianJNI.Plo +1 -0
- data/java/native/.deps/utils.Plo +1 -0
- data/java/native/Database.cc +222 -0
- data/java/native/Document.cc +173 -0
- data/java/native/ESet.cc +79 -0
- data/java/native/ESetIterator.cc +82 -0
- data/java/native/Enquire.cc +271 -0
- data/java/native/MSet.cc +169 -0
- data/java/native/MSetIterator.cc +107 -0
- data/java/native/Makefile +594 -0
- data/java/native/Makefile.am +51 -0
- data/java/native/Makefile.in +594 -0
- data/java/native/PositionIterator.cc +64 -0
- data/java/native/Query.cc +180 -0
- data/java/native/RSet.cc +98 -0
- data/java/native/Stem.cc +75 -0
- data/java/native/TermIterator.cc +107 -0
- data/java/native/WritableDatabase.cc +118 -0
- data/java/native/XapianObjectHolder.h +115 -0
- data/java/native/org_xapian_XapianJNI.cc +78 -0
- data/java/native/org_xapian_XapianJNI.h +1369 -0
- data/java/native/utils.cc +51 -0
- data/java/native/xapian_jni.h +116 -0
- data/java/org/xapian/Database.java +148 -0
- data/java/org/xapian/Document.java +135 -0
- data/java/org/xapian/ESet.java +66 -0
- data/java/org/xapian/ESetIterator.java +97 -0
- data/java/org/xapian/Enquire.java +136 -0
- data/java/org/xapian/ExpandDecider.java +30 -0
- data/java/org/xapian/MSet.java +104 -0
- data/java/org/xapian/MSetIterator.java +132 -0
- data/java/org/xapian/Makefile +580 -0
- data/java/org/xapian/Makefile.am +38 -0
- data/java/org/xapian/Makefile.in +580 -0
- data/java/org/xapian/MatchDecider.java +30 -0
- data/java/org/xapian/PositionIterator.java +89 -0
- data/java/org/xapian/Query.java +190 -0
- data/java/org/xapian/RSet.java +89 -0
- data/java/org/xapian/Stem.java +80 -0
- data/java/org/xapian/TermIterator.java +142 -0
- data/java/org/xapian/WritableDatabase.java +92 -0
- data/java/org/xapian/Xapian.java +114 -0
- data/java/org/xapian/XapianJNI.java +444 -0
- data/java/org/xapian/errors/AssertionError.java +40 -0
- data/java/org/xapian/errors/DatabaseCorruptError.java +40 -0
- data/java/org/xapian/errors/DatabaseError.java +40 -0
- data/java/org/xapian/errors/DatabaseLockError.java +40 -0
- data/java/org/xapian/errors/DatabaseModifiedError.java +40 -0
- data/java/org/xapian/errors/DatabaseOpeningError.java +40 -0
- data/java/org/xapian/errors/DocNotFoundError.java +40 -0
- data/java/org/xapian/errors/FeatureUnavailableError.java +40 -0
- data/java/org/xapian/errors/InternalError.java +40 -0
- data/java/org/xapian/errors/InvalidArgumentError.java +40 -0
- data/java/org/xapian/errors/InvalidOperationError.java +40 -0
- data/java/org/xapian/errors/LogicError.java +40 -0
- data/java/org/xapian/errors/Makefile +416 -0
- data/java/org/xapian/errors/Makefile.am +32 -0
- data/java/org/xapian/errors/Makefile.in +416 -0
- data/java/org/xapian/errors/NetworkError.java +40 -0
- data/java/org/xapian/errors/NetworkTimeoutError.java +40 -0
- data/java/org/xapian/errors/RangeError.java +40 -0
- data/java/org/xapian/errors/RuntimeError.java +40 -0
- data/java/org/xapian/errors/UnimplementedError.java +40 -0
- data/java/org/xapian/errors/XapianError.java +40 -0
- data/java/org/xapian/errors/XapianRuntimeError.java +49 -0
- data/java/org/xapian/examples/Makefile +391 -0
- data/java/org/xapian/examples/Makefile.am +8 -0
- data/java/org/xapian/examples/Makefile.in +391 -0
- data/java/org/xapian/examples/SimpleIndex.java +68 -0
- data/java/org/xapian/examples/SimpleSearch.java +71 -0
- data/java/run-java-test +6 -0
- data/libtool +7618 -0
- data/ltmain.sh +6956 -0
- data/mhs-xapian.gemspec +368 -0
- data/missing +378 -0
- data/php/.deps/xapian_wrap.Plo +1 -0
- data/php/Makefile +871 -0
- data/php/Makefile.am +82 -0
- data/php/Makefile.in +871 -0
- data/php/docs/Makefile +453 -0
- data/php/docs/Makefile.am +19 -0
- data/php/docs/Makefile.in +453 -0
- data/php/docs/examples/simpleexpand.php4 +108 -0
- data/php/docs/examples/simpleexpand.php5 +104 -0
- data/php/docs/examples/simpleindex.php4 +76 -0
- data/php/docs/examples/simpleindex.php5 +73 -0
- data/php/docs/examples/simplesearch.php4 +75 -0
- data/php/docs/examples/simplesearch.php5 +72 -0
- data/php/docs/index.html +313 -0
- data/php/except.i +98 -0
- data/php/php4/php_xapian.h +323 -0
- data/php/php4/xapian.php +32 -0
- data/php/php4/xapian_wrap.cc +27656 -0
- data/php/php5/php_xapian.h +319 -0
- data/php/php5/xapian.php +1566 -0
- data/php/php5/xapian_wrap.cc +24330 -0
- data/php/smoketest.php +246 -0
- data/php/smoketest4.php +84 -0
- data/php/smoketest5.php +79 -0
- data/php/util.i +187 -0
- data/python/.deps/xapian_wrap.Plo +1 -0
- data/python/Makefile +891 -0
- data/python/Makefile.am +105 -0
- data/python/Makefile.in +891 -0
- data/python/doccomments.i +5134 -0
- data/python/docs/Makefile +448 -0
- data/python/docs/Makefile.am +14 -0
- data/python/docs/Makefile.in +448 -0
- data/python/docs/examples/simpleexpand.py +98 -0
- data/python/docs/examples/simpleindex.py +65 -0
- data/python/docs/examples/simplematchdecider.py +78 -0
- data/python/docs/examples/simplesearch.py +65 -0
- data/python/docs/index.html +420 -0
- data/python/except.i +290 -0
- data/python/extra.i +1048 -0
- data/python/extracomments.i +28 -0
- data/python/generate-python-exceptions +189 -0
- data/python/generate-python-exceptions.in +189 -0
- data/python/modern/xapian.py +5662 -0
- data/python/modern/xapian_wrap.cc +35170 -0
- data/python/modern/xapian_wrap.h +244 -0
- data/python/pythontest.py +1110 -0
- data/python/smoketest.py +328 -0
- data/python/testsuite.py +382 -0
- data/python/util.i +517 -0
- data/ruby/.deps/xapian_wrap.Plo +494 -0
- data/ruby/.libs/_xapian.bundle +0 -0
- data/ruby/.libs/_xapian.bundle.dSYM/Contents/Info.plist +25 -0
- data/ruby/.libs/_xapian.bundle.dSYM/Contents/Resources/DWARF/_xapian.bundle +0 -0
- data/ruby/.libs/_xapian.la +35 -0
- data/ruby/.libs/_xapian.lai +35 -0
- data/ruby/Makefile +854 -0
- data/ruby/Makefile.am +62 -0
- data/ruby/Makefile.in +854 -0
- data/ruby/_xapian.la +35 -0
- data/ruby/docs/Makefile +487 -0
- data/ruby/docs/Makefile.am +50 -0
- data/ruby/docs/Makefile.in +487 -0
- data/ruby/docs/examples/simpleexpand.rb +98 -0
- data/ruby/docs/examples/simpleindex.rb +60 -0
- data/ruby/docs/examples/simplematchdecider.rb +74 -0
- data/ruby/docs/examples/simplesearch.rb +63 -0
- data/ruby/docs/index.html +197 -0
- data/ruby/smoketest.rb +211 -0
- data/ruby/util.i +232 -0
- data/ruby/xapian.rb +280 -0
- data/ruby/xapian_wrap.cc +25837 -0
- data/ruby/xapian_wrap.h +65 -0
- data/ruby/xapian_wrap.lo +12 -0
- data/skiptest +2 -0
- data/stamp-h1 +1 -0
- data/tcl8/.deps/xapian_wrap.Plo +1 -0
- data/tcl8/Makefile +835 -0
- data/tcl8/Makefile.am +49 -0
- data/tcl8/Makefile.in +835 -0
- data/tcl8/docs/Makefile +448 -0
- data/tcl8/docs/Makefile.am +14 -0
- data/tcl8/docs/Makefile.in +448 -0
- data/tcl8/docs/examples/simpleexpand.tcl +104 -0
- data/tcl8/docs/examples/simpleindex.tcl +68 -0
- data/tcl8/docs/examples/simplesearch.tcl +66 -0
- data/tcl8/docs/index.html +208 -0
- data/tcl8/except.i +48 -0
- data/tcl8/pkgIndex.tcl +1 -0
- data/tcl8/pkgIndex.tcl.in +1 -0
- data/tcl8/run-tcl-test +15 -0
- data/tcl8/runtest.tcl +29 -0
- data/tcl8/smoketest.tcl +155 -0
- data/tcl8/util.i +76 -0
- data/tcl8/xapian_wrap.cc +20900 -0
- data/xapian-bindings.spec +206 -0
- data/xapian-bindings.spec.in +206 -0
- data/xapian-version.h +1 -0
- data/xapian-version.h.in +1 -0
- data/xapian.i +939 -0
- metadata +395 -0
data/python/smoketest.py
ADDED
@@ -0,0 +1,328 @@
|
|
1
|
+
# Simple test to ensure that we can load the xapian module and exercise basic
|
2
|
+
# functionality successfully.
|
3
|
+
#
|
4
|
+
# Copyright (C) 2004,2005,2006,2007 Olly Betts
|
5
|
+
# Copyright (C) 2007 Lemur Consulting Ltd
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License as
|
9
|
+
# published by the Free Software Foundation; either version 2 of the
|
10
|
+
# License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
20
|
+
# USA
|
21
|
+
|
22
|
+
import sys
|
23
|
+
import xapian
|
24
|
+
|
25
|
+
from testsuite import *
|
26
|
+
|
27
|
+
def test_all():
|
28
|
+
# Test the version number reporting functions give plausible results.
|
29
|
+
v = "%d.%d.%d" % (xapian.major_version(),
|
30
|
+
xapian.minor_version(),
|
31
|
+
xapian.revision())
|
32
|
+
v2 = xapian.version_string()
|
33
|
+
expect(v2, v, "Unexpected version output")
|
34
|
+
|
35
|
+
stem = xapian.Stem("english")
|
36
|
+
expect(stem.get_description(), "Xapian::Stem(english)", "Unexpected stem.get_description()")
|
37
|
+
|
38
|
+
doc = xapian.Document()
|
39
|
+
doc.set_data("a\0b")
|
40
|
+
if doc.get_data() == "a":
|
41
|
+
raise TestFail("get_data+set_data truncates at a zero byte")
|
42
|
+
expect(doc.get_data(), "a\0b", "get_data+set_data doesn't transparently handle a zero byte")
|
43
|
+
doc.set_data("is there anybody out there?")
|
44
|
+
doc.add_term("XYzzy")
|
45
|
+
doc.add_posting(stem("is"), 1)
|
46
|
+
doc.add_posting(stem("there"), 2)
|
47
|
+
doc.add_posting(stem("anybody"), 3)
|
48
|
+
doc.add_posting(stem("out"), 4)
|
49
|
+
doc.add_posting(stem("there"), 5)
|
50
|
+
|
51
|
+
db = xapian.inmemory_open()
|
52
|
+
db.add_document(doc)
|
53
|
+
expect(db.get_doccount(), 1, "Unexpected db.get_doccount()")
|
54
|
+
terms = ["smoke", "test", "terms"]
|
55
|
+
expect_query(xapian.Query(xapian.Query.OP_OR, terms),
|
56
|
+
"(smoke OR test OR terms)")
|
57
|
+
query1 = xapian.Query(xapian.Query.OP_PHRASE, ("smoke", "test", "tuple"))
|
58
|
+
query2 = xapian.Query(xapian.Query.OP_XOR, (xapian.Query("smoke"), query1, "string"))
|
59
|
+
expect_query(query1, "(smoke PHRASE 3 test PHRASE 3 tuple)")
|
60
|
+
expect_query(query2, "(smoke XOR (smoke PHRASE 3 test PHRASE 3 tuple) XOR string)")
|
61
|
+
subqs = ["a", "b"]
|
62
|
+
expect_query(xapian.Query(xapian.Query.OP_OR, subqs), "(a OR b)")
|
63
|
+
expect_query(xapian.Query(xapian.Query.OP_VALUE_RANGE, 0, '1', '4'),
|
64
|
+
"VALUE_RANGE 0 1 4")
|
65
|
+
|
66
|
+
# Feature test for Query.__iter__
|
67
|
+
term_count = 0
|
68
|
+
for term in query2:
|
69
|
+
term_count += 1
|
70
|
+
expect(term_count, 4, "Unexpected number of terms in query2")
|
71
|
+
|
72
|
+
enq = xapian.Enquire(db)
|
73
|
+
enq.set_query(xapian.Query(xapian.Query.OP_OR, "there", "is"))
|
74
|
+
mset = enq.get_mset(0, 10)
|
75
|
+
expect(mset.size(), 1, "Unexpected mset.size()")
|
76
|
+
|
77
|
+
# Feature test for Enquire.matching_terms(docid)
|
78
|
+
term_count = 0
|
79
|
+
for term in enq.matching_terms(mset.get_hit(0)):
|
80
|
+
term_count += 1
|
81
|
+
expect(term_count, 2, "Unexpected number of matching terms")
|
82
|
+
|
83
|
+
# Feature test for MSet.__iter__
|
84
|
+
msize = 0
|
85
|
+
for match in mset:
|
86
|
+
msize += 1
|
87
|
+
expect(msize, mset.size(), "Unexpected number of entries in mset")
|
88
|
+
|
89
|
+
terms = " ".join(enq.matching_terms(mset.get_hit(0)))
|
90
|
+
expect(terms, "is there", "Unexpected terms")
|
91
|
+
|
92
|
+
# Feature test for ESet.__iter__
|
93
|
+
rset = xapian.RSet()
|
94
|
+
rset.add_document(1)
|
95
|
+
eset = enq.get_eset(10, rset)
|
96
|
+
term_count = 0
|
97
|
+
for term in eset:
|
98
|
+
term_count += 1
|
99
|
+
expect(term_count, 3, "Unexpected number of expand terms")
|
100
|
+
|
101
|
+
# Feature test for Database.__iter__
|
102
|
+
term_count = 0
|
103
|
+
for term in db:
|
104
|
+
term_count += 1
|
105
|
+
expect(term_count, 5, "Unexpected number of terms in db")
|
106
|
+
|
107
|
+
# Feature test for Database.allterms
|
108
|
+
term_count = 0
|
109
|
+
for term in db.allterms():
|
110
|
+
term_count += 1
|
111
|
+
expect(term_count, 5, "Unexpected number of terms in db.allterms")
|
112
|
+
|
113
|
+
# Feature test for Database.postlist
|
114
|
+
count = 0
|
115
|
+
for posting in db.postlist("there"):
|
116
|
+
count += 1
|
117
|
+
expect(count, 1, "Unexpected number of entries in db.postlist('there')")
|
118
|
+
|
119
|
+
# Feature test for Database.postlist with empty term (alldocspostlist)
|
120
|
+
count = 0
|
121
|
+
for posting in db.postlist(""):
|
122
|
+
count += 1
|
123
|
+
expect(count, 1, "Unexpected number of entries in db.postlist('')")
|
124
|
+
|
125
|
+
# Feature test for Database.termlist
|
126
|
+
count = 0
|
127
|
+
for term in db.termlist(1):
|
128
|
+
count += 1
|
129
|
+
expect(count, 5, "Unexpected number of entries in db.termlist(1)")
|
130
|
+
|
131
|
+
# Feature test for Database.positionlist
|
132
|
+
count = 0
|
133
|
+
for term in db.positionlist(1, "there"):
|
134
|
+
count += 1
|
135
|
+
expect(count, 2, "Unexpected number of entries in db.positionlist(1, 'there')")
|
136
|
+
|
137
|
+
# Feature test for Document.termlist
|
138
|
+
count = 0
|
139
|
+
for term in doc.termlist():
|
140
|
+
count += 1
|
141
|
+
expect(count, 5, "Unexpected number of entries in doc.termlist()")
|
142
|
+
|
143
|
+
# Feature test for TermIter.skip_to
|
144
|
+
term = doc.termlist()
|
145
|
+
term.skip_to('n')
|
146
|
+
while True:
|
147
|
+
try:
|
148
|
+
x = term.next()
|
149
|
+
except StopIteration:
|
150
|
+
break
|
151
|
+
if x.term < 'n':
|
152
|
+
raise TestFail("TermIter.skip_to didn't skip term '%s'" % x.term)
|
153
|
+
|
154
|
+
# Feature test for Document.values
|
155
|
+
count = 0
|
156
|
+
for term in doc.values():
|
157
|
+
count += 1
|
158
|
+
expect(count, 0, "Unexpected number of entries in doc.values")
|
159
|
+
|
160
|
+
# Check exception handling for Xapian::DocNotFoundError
|
161
|
+
expect_exception(xapian.DocNotFoundError, "Docid 3 not found", db.get_document, 3)
|
162
|
+
|
163
|
+
# Check value of OP_ELITE_SET
|
164
|
+
expect(xapian.Query.OP_ELITE_SET, 10, "Unexpected value for OP_ELITE_SET")
|
165
|
+
|
166
|
+
# Feature test for MatchDecider
|
167
|
+
doc = xapian.Document()
|
168
|
+
doc.set_data("Two")
|
169
|
+
doc.add_posting(stem("out"), 1)
|
170
|
+
doc.add_posting(stem("outside"), 1)
|
171
|
+
doc.add_posting(stem("source"), 2)
|
172
|
+
doc.add_value(0, "yes")
|
173
|
+
db.add_document(doc)
|
174
|
+
|
175
|
+
class testmatchdecider(xapian.MatchDecider):
|
176
|
+
def __call__(self, doc):
|
177
|
+
return doc.get_value(0) == "yes"
|
178
|
+
|
179
|
+
query = xapian.Query(stem("out"))
|
180
|
+
enquire = xapian.Enquire(db)
|
181
|
+
enquire.set_query(query)
|
182
|
+
mset = enquire.get_mset(0, 10, None, testmatchdecider())
|
183
|
+
expect(mset.size(), 1, "Unexpected number of documents returned by match decider")
|
184
|
+
expect(mset.get_docid(0), 2, "MatchDecider mset has wrong docid in")
|
185
|
+
|
186
|
+
# Feature test for ExpandDecider
|
187
|
+
class testexpanddecider(xapian.ExpandDecider):
|
188
|
+
def __call__(self, term):
|
189
|
+
return (not term.startswith('a'))
|
190
|
+
|
191
|
+
enquire = xapian.Enquire(db)
|
192
|
+
rset = xapian.RSet()
|
193
|
+
rset.add_document(1)
|
194
|
+
eset = enquire.get_eset(10, rset, xapian.Enquire.USE_EXACT_TERMFREQ, 1.0, testexpanddecider())
|
195
|
+
eset_terms = [term[xapian.ESET_TNAME] for term in eset.items]
|
196
|
+
expect(len(eset_terms), eset.size(), "Unexpected number of terms returned by expand")
|
197
|
+
if filter(lambda t: t.startswith('a'), eset_terms):
|
198
|
+
raise TestFail("ExpandDecider was not used")
|
199
|
+
|
200
|
+
# Check QueryParser parsing error.
|
201
|
+
qp = xapian.QueryParser()
|
202
|
+
expect_exception(xapian.QueryParserError, "Syntax: <expression> AND <expression>", qp.parse_query, "test AND")
|
203
|
+
|
204
|
+
# Check QueryParser pure NOT option
|
205
|
+
qp = xapian.QueryParser()
|
206
|
+
expect_query(qp.parse_query("NOT test", qp.FLAG_BOOLEAN + qp.FLAG_PURE_NOT),
|
207
|
+
"(<alldocuments> AND_NOT test:(pos=1))")
|
208
|
+
|
209
|
+
# Check QueryParser partial option
|
210
|
+
qp = xapian.QueryParser()
|
211
|
+
qp.set_database(db)
|
212
|
+
qp.set_default_op(xapian.Query.OP_AND)
|
213
|
+
qp.set_stemming_strategy(qp.STEM_SOME)
|
214
|
+
qp.set_stemmer(xapian.Stem('en'))
|
215
|
+
expect_query(qp.parse_query("foo o", qp.FLAG_PARTIAL),
|
216
|
+
"(Zfoo:(pos=1) AND (out:(pos=2) OR outsid:(pos=2) OR Zo:(pos=2)))")
|
217
|
+
|
218
|
+
expect_query(qp.parse_query("foo outside", qp.FLAG_PARTIAL),
|
219
|
+
"(Zfoo:(pos=1) AND Zoutsid:(pos=2))")
|
220
|
+
|
221
|
+
# Test supplying unicode strings
|
222
|
+
expect_query(xapian.Query(xapian.Query.OP_OR, (u'foo', u'bar')),
|
223
|
+
'(foo OR bar)')
|
224
|
+
expect_query(xapian.Query(xapian.Query.OP_OR, ('foo', u'bar\xa3')),
|
225
|
+
'(foo OR bar\xc2\xa3)')
|
226
|
+
expect_query(xapian.Query(xapian.Query.OP_OR, ('foo', 'bar\xc2\xa3')),
|
227
|
+
'(foo OR bar\xc2\xa3)')
|
228
|
+
expect_query(xapian.Query(xapian.Query.OP_OR, u'foo', u'bar'),
|
229
|
+
'(foo OR bar)')
|
230
|
+
|
231
|
+
expect_query(qp.parse_query(u"NOT t\xe9st", qp.FLAG_BOOLEAN + qp.FLAG_PURE_NOT),
|
232
|
+
"(<alldocuments> AND_NOT Zt\xc3\xa9st:(pos=1))")
|
233
|
+
|
234
|
+
doc = xapian.Document()
|
235
|
+
doc.set_data(u"Unicode with an acc\xe9nt")
|
236
|
+
doc.add_posting(stem(u"out\xe9r"), 1)
|
237
|
+
expect(doc.get_data(), u"Unicode with an acc\xe9nt".encode('utf-8'))
|
238
|
+
term = doc.termlist().next().term
|
239
|
+
expect(term, u"out\xe9r".encode('utf-8'))
|
240
|
+
|
241
|
+
# Check simple stopper
|
242
|
+
stop = xapian.SimpleStopper()
|
243
|
+
qp.set_stopper(stop)
|
244
|
+
expect(stop('a'), False)
|
245
|
+
expect_query(qp.parse_query(u"foo bar a", qp.FLAG_BOOLEAN),
|
246
|
+
"(Zfoo:(pos=1) AND Zbar:(pos=2) AND Za:(pos=3))")
|
247
|
+
|
248
|
+
stop.add('a')
|
249
|
+
expect(stop('a'), True)
|
250
|
+
expect_query(qp.parse_query(u"foo bar a", qp.FLAG_BOOLEAN),
|
251
|
+
"(Zfoo:(pos=1) AND Zbar:(pos=2))")
|
252
|
+
|
253
|
+
# Feature test for custom Stopper
|
254
|
+
class my_b_stopper(xapian.Stopper):
|
255
|
+
def __call__(self, term):
|
256
|
+
return term == "b"
|
257
|
+
|
258
|
+
def get_description(self):
|
259
|
+
return u"my_b_stopper"
|
260
|
+
|
261
|
+
stop = my_b_stopper()
|
262
|
+
expect(stop.get_description(), u"my_b_stopper")
|
263
|
+
qp.set_stopper(stop)
|
264
|
+
expect(stop('a'), False)
|
265
|
+
expect_query(qp.parse_query(u"foo bar a", qp.FLAG_BOOLEAN),
|
266
|
+
"(Zfoo:(pos=1) AND Zbar:(pos=2) AND Za:(pos=3))")
|
267
|
+
|
268
|
+
expect(stop('b'), True)
|
269
|
+
expect_query(qp.parse_query(u"foo bar b", qp.FLAG_BOOLEAN),
|
270
|
+
"(Zfoo:(pos=1) AND Zbar:(pos=2))")
|
271
|
+
|
272
|
+
# Test TermGenerator
|
273
|
+
termgen = xapian.TermGenerator()
|
274
|
+
doc = xapian.Document()
|
275
|
+
termgen.set_document(doc)
|
276
|
+
termgen.index_text('foo bar baz foo')
|
277
|
+
expect([(item.term, item.wdf, [pos for pos in item.positer]) for item in doc.termlist()], [('bar', 1, [2]), ('baz', 1, [3]), ('foo', 2, [1, 4])])
|
278
|
+
|
279
|
+
|
280
|
+
# Check DateValueRangeProcessor works
|
281
|
+
context("checking that DateValueRangeProcessor works")
|
282
|
+
qp = xapian.QueryParser()
|
283
|
+
vrpdate = xapian.DateValueRangeProcessor(1, 1, 1960)
|
284
|
+
qp.add_valuerangeprocessor(vrpdate)
|
285
|
+
query = qp.parse_query('12/03/99..12/04/01')
|
286
|
+
expect(str(query), 'Xapian::Query(VALUE_RANGE 1 19991203 20011204)')
|
287
|
+
|
288
|
+
# Regression test for bug#193, fixed in 1.0.3.
|
289
|
+
context("running regression test for bug#193")
|
290
|
+
vrp = xapian.NumberValueRangeProcessor(0, '$', True)
|
291
|
+
a = '$10'
|
292
|
+
b = '20'
|
293
|
+
slot, a, b = vrp(a, b)
|
294
|
+
expect(slot, 0)
|
295
|
+
expect(xapian.sortable_unserialise(a), 10)
|
296
|
+
expect(xapian.sortable_unserialise(b), 20)
|
297
|
+
|
298
|
+
# Regression tests copied from PHP (probably always worked in python, but
|
299
|
+
# let's check...)
|
300
|
+
context("running regression tests for issues which were found in PHP")
|
301
|
+
|
302
|
+
# PHP overload resolution involving boolean types failed.
|
303
|
+
enq.set_sort_by_value(1, True)
|
304
|
+
|
305
|
+
# Regression test - fixed in 0.9.10.1.
|
306
|
+
oqparser = xapian.QueryParser()
|
307
|
+
oquery = oqparser.parse_query("I like tea")
|
308
|
+
|
309
|
+
# Regression test for bug#192 - fixed in 1.0.3.
|
310
|
+
enq.set_cutoff(100)
|
311
|
+
|
312
|
+
# Test setting and getting metadata
|
313
|
+
expect(db.get_metadata('Foo'), '')
|
314
|
+
db.set_metadata('Foo', 'Foo')
|
315
|
+
expect(db.get_metadata('Foo'), 'Foo')
|
316
|
+
expect_exception(xapian.InvalidArgumentError, "Empty metadata keys are invalid", db.get_metadata, '')
|
317
|
+
expect_exception(xapian.InvalidArgumentError, "Empty metadata keys are invalid", db.set_metadata, '', 'Foo')
|
318
|
+
expect_exception(xapian.InvalidArgumentError, "Empty metadata keys are invalid", db.get_metadata, '')
|
319
|
+
|
320
|
+
# Test OP_SCALE_WEIGHT and corresponding constructor
|
321
|
+
expect_query(xapian.Query(xapian.Query.OP_SCALE_WEIGHT, xapian.Query('foo'), 5),
|
322
|
+
"5 * foo")
|
323
|
+
|
324
|
+
# Run all tests (ie, callables with names starting "test_").
|
325
|
+
if not runtests(globals()):
|
326
|
+
sys.exit(1)
|
327
|
+
|
328
|
+
# vim:syntax=python:set expandtab:
|
data/python/testsuite.py
ADDED
@@ -0,0 +1,382 @@
|
|
1
|
+
# Utility functions for running tests and reporting the results.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2007 Lemur Consulting Ltd
|
4
|
+
#
|
5
|
+
# This program is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU General Public License as
|
7
|
+
# published by the Free Software Foundation; either version 2 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
18
|
+
# USA
|
19
|
+
|
20
|
+
import os as _os
|
21
|
+
import os.path as _path
|
22
|
+
import sys as _sys
|
23
|
+
import traceback as _traceback
|
24
|
+
import xapian as _xapian
|
25
|
+
|
26
|
+
class TestFail(Exception):
|
27
|
+
pass
|
28
|
+
|
29
|
+
class TestRunner(object):
|
30
|
+
def __init__(self):
|
31
|
+
"""Initialise the TestRunner.
|
32
|
+
|
33
|
+
"""
|
34
|
+
|
35
|
+
self._out = OutProxy(_sys.stdout)
|
36
|
+
|
37
|
+
# _verbose is an integer, higher meaning more verbose
|
38
|
+
self._verbose = _os.environ.get('VERBOSE', '').lower()
|
39
|
+
if self._verbose in ('', '0', 'no', 'off', 'false'):
|
40
|
+
self._verbose = 0
|
41
|
+
else:
|
42
|
+
try:
|
43
|
+
self._verbose = int(self._verbose)
|
44
|
+
except:
|
45
|
+
self._verbose = 1
|
46
|
+
|
47
|
+
# context is a description of what the test is currently checking
|
48
|
+
self._context = None
|
49
|
+
|
50
|
+
def context(self, context):
|
51
|
+
"""Set the context.
|
52
|
+
|
53
|
+
This should be a string describing what a test is checking, and will be
|
54
|
+
displayed if the test fails.
|
55
|
+
|
56
|
+
A test may change the context several times - each call will override
|
57
|
+
subsequent calls.
|
58
|
+
|
59
|
+
Set the context to None to remove display of a specific context message.
|
60
|
+
This is performed automatically at the start of each test.
|
61
|
+
|
62
|
+
"""
|
63
|
+
self._context = context
|
64
|
+
if context is not None and self._verbose > 1:
|
65
|
+
self._out.start_line()
|
66
|
+
self._out.write("Context: %s\n" % context)
|
67
|
+
self._out.flush()
|
68
|
+
|
69
|
+
def expect(self, got, expected, message="Expected equality"):
|
70
|
+
"""Function used to check for a particular expected value.
|
71
|
+
|
72
|
+
"""
|
73
|
+
if self._verbose > 2:
|
74
|
+
self._out.start_line()
|
75
|
+
self._out.write("Checking for %r: expecting %r ... " % (message, expected))
|
76
|
+
self._out.flush()
|
77
|
+
if got != expected:
|
78
|
+
if self._verbose > 2:
|
79
|
+
self._out.write_colour(" #red#failed##")
|
80
|
+
self._out.write(": got %r\n" % got)
|
81
|
+
self._out.flush()
|
82
|
+
raise TestFail("%s: got %r, expected %r" % (message, got, expected))
|
83
|
+
if self._verbose > 2:
|
84
|
+
self._out.write_colour(" #green#ok##\n")
|
85
|
+
self._out.flush()
|
86
|
+
|
87
|
+
def expect_query(self, query, expected):
|
88
|
+
"""Check that the description of a query is as expected.
|
89
|
+
|
90
|
+
"""
|
91
|
+
expected = 'Xapian::Query(' + expected + ')'
|
92
|
+
desc = query.get_description()
|
93
|
+
if self._verbose > 2:
|
94
|
+
self._out.start_line()
|
95
|
+
self._out.write("Checking query.get_description(): expecting %r ... " % expected)
|
96
|
+
self._out.flush()
|
97
|
+
if desc != expected:
|
98
|
+
if self._verbose > 2:
|
99
|
+
self._out.write_colour(" #red#failed##")
|
100
|
+
self._out.write(": got %r\n" % desc)
|
101
|
+
self._out.flush()
|
102
|
+
raise TestFail("Unexpected query.get_description(): got %r, expected %r" % (desc, expected))
|
103
|
+
if self._verbose > 2:
|
104
|
+
self._out.write_colour(" #green#ok##\n")
|
105
|
+
self._out.flush()
|
106
|
+
|
107
|
+
def expect_exception(self, expectedclass, expectedmsg, callable, *args):
|
108
|
+
if self._verbose > 2:
|
109
|
+
self._out.start_line()
|
110
|
+
self._out.write("Checking for exception: %s(%r) ... " % (str(expectedclass), expectedmsg))
|
111
|
+
self._out.flush()
|
112
|
+
try:
|
113
|
+
callable(*args)
|
114
|
+
if self._verbose > 2:
|
115
|
+
self._out.write_colour(" #red#failed##: no exception occurred\n")
|
116
|
+
self._out.flush()
|
117
|
+
raise TestFail("Expected %s(%r) exception" % (str(expectedclass), expectedmsg))
|
118
|
+
except expectedclass, e:
|
119
|
+
if str(e) != expectedmsg:
|
120
|
+
if self._verbose > 2:
|
121
|
+
self._out.write_colour(" #red#failed##")
|
122
|
+
self._out.write(": exception string not as expected: got '%s'\n" % str(e))
|
123
|
+
self._out.flush()
|
124
|
+
raise TestFail("Exception string not as expected: got '%s', expected '%s'" % (str(e), expectedmsg))
|
125
|
+
if e.__class__ != expectedclass:
|
126
|
+
if self._verbose > 2:
|
127
|
+
self._out.write_colour(" #red#failed##")
|
128
|
+
self._out.write(": didn't get right exception class: got '%s'\n" % str(e.__class__))
|
129
|
+
self._out.flush()
|
130
|
+
raise TestFail("Didn't get right exception class: got '%s', expected '%s'" % (str(e.__class__), str(expectedclass)))
|
131
|
+
if self._verbose > 2:
|
132
|
+
self._out.write_colour(" #green#ok##\n")
|
133
|
+
self._out.flush()
|
134
|
+
|
135
|
+
def report_failure(self, name, msg, show_traceback=True):
|
136
|
+
"Report a test failure, with some useful context."
|
137
|
+
|
138
|
+
orig_tb = _traceback.extract_tb(_sys.exc_info()[2])
|
139
|
+
tb = orig_tb
|
140
|
+
|
141
|
+
# Move up the traceback until we get to the line in the test
|
142
|
+
# function which caused the failure.
|
143
|
+
while tb[-1][2] != 'test_' + name:
|
144
|
+
tb = tb[:-1]
|
145
|
+
|
146
|
+
# Display the context in the text function.
|
147
|
+
filepath, linenum, functionname, text = tb[-1]
|
148
|
+
filename = _os.path.basename(filepath)
|
149
|
+
|
150
|
+
self._out.ensure_space()
|
151
|
+
self._out.write_colour("#red#FAILED##\n")
|
152
|
+
if self._verbose > 0:
|
153
|
+
if self._context is None:
|
154
|
+
context = ''
|
155
|
+
else:
|
156
|
+
context = ", when %s" % self._context
|
157
|
+
firstline = "%s:%d" % (filename, linenum)
|
158
|
+
self._out.write("\n%s:%s%s\n" % (firstline, msg, context))
|
159
|
+
|
160
|
+
# Display sourcecode lines
|
161
|
+
lines = open(filepath).readlines()
|
162
|
+
startline = max(linenum - 3, 0)
|
163
|
+
endline = min(linenum + 2, len(lines))
|
164
|
+
for num in xrange(startline, endline):
|
165
|
+
if num + 1 == linenum:
|
166
|
+
self._out.write('->')
|
167
|
+
else:
|
168
|
+
self._out.write(' ')
|
169
|
+
self._out.write("%4d %s\n" % (num + 1, lines[num].rstrip()))
|
170
|
+
|
171
|
+
# Display the traceback
|
172
|
+
if show_traceback:
|
173
|
+
self._out.write("Traceback (most recent call last):\n")
|
174
|
+
for line in _traceback.format_list(orig_tb):
|
175
|
+
self._out.write(line.rstrip() + '\n')
|
176
|
+
self._out.write('\n')
|
177
|
+
|
178
|
+
# Display some information about the xapian version and platform
|
179
|
+
self._out.write("Xapian version: %s\n" % _xapian.version_string())
|
180
|
+
try:
|
181
|
+
import platform
|
182
|
+
platdesc = "%s %s (%s)" % platform.system_alias(platform.system(),
|
183
|
+
platform.release(),
|
184
|
+
platform.version())
|
185
|
+
self._out.write("Platform: %s\n" % platdesc)
|
186
|
+
except:
|
187
|
+
pass
|
188
|
+
self._out.write('\nWhen reporting this problem, please quote all the preceding lines from\n"%s" onwards.\n\n' % firstline)
|
189
|
+
|
190
|
+
self._out.flush()
|
191
|
+
|
192
|
+
def runtest(self, name, test_fn):
|
193
|
+
"""Run a single test.
|
194
|
+
|
195
|
+
"""
|
196
|
+
startline = "Running test: %s..." % name
|
197
|
+
self._out.write(startline)
|
198
|
+
self._out.flush()
|
199
|
+
try:
|
200
|
+
test_fn()
|
201
|
+
if self._verbose > 0 or self._out.plain:
|
202
|
+
self._out.ensure_space()
|
203
|
+
self._out.write_colour("#green#ok##\n")
|
204
|
+
else:
|
205
|
+
self._out.clear_line()
|
206
|
+
self._out.flush()
|
207
|
+
return True
|
208
|
+
except TestFail, e:
|
209
|
+
self.report_failure(name, str(e), show_traceback=False)
|
210
|
+
except _xapian.Error, e:
|
211
|
+
self.report_failure(name, "%s: %s" % (str(e.__class__), str(e)))
|
212
|
+
except Exception, e:
|
213
|
+
self.report_failure(name, "%s: %s" % (str(e.__class__), str(e)))
|
214
|
+
return False
|
215
|
+
|
216
|
+
def runtests(self, namedict, runonly=None):
|
217
|
+
"""Run a set of tests.
|
218
|
+
|
219
|
+
Takes a dictionary of name-value pairs and runs all the values which are
|
220
|
+
callables, for which the name begins with "test_".
|
221
|
+
|
222
|
+
Typical usage is to pass "locals()" as the parameter, to run all callables
|
223
|
+
with names starting "test_" in local scope.
|
224
|
+
|
225
|
+
If runonly is supplied, and non-empty, only those tests which appear in
|
226
|
+
runonly will be run.
|
227
|
+
|
228
|
+
"""
|
229
|
+
tests = []
|
230
|
+
if isinstance(namedict, dict):
|
231
|
+
for name in namedict:
|
232
|
+
if name.startswith('test_'):
|
233
|
+
fn = namedict[name]
|
234
|
+
name = name[5:]
|
235
|
+
if callable(fn):
|
236
|
+
tests.append((name, fn))
|
237
|
+
tests.sort()
|
238
|
+
else:
|
239
|
+
tests = namedict
|
240
|
+
|
241
|
+
if runonly is not None and len(runonly) != 0:
|
242
|
+
oldtests = tests
|
243
|
+
tests = []
|
244
|
+
for name, fn in oldtests:
|
245
|
+
if name in runonly:
|
246
|
+
tests.append((name, fn))
|
247
|
+
|
248
|
+
passed, failed = 0, 0
|
249
|
+
for name, fn in tests:
|
250
|
+
self.context(None)
|
251
|
+
if self.runtest(name, fn):
|
252
|
+
passed += 1
|
253
|
+
else:
|
254
|
+
failed += 1
|
255
|
+
if failed:
|
256
|
+
if self._verbose == 0:
|
257
|
+
self._out.write('Re-run with the VERBOSE environment variable set to "1" to see details.\n')
|
258
|
+
self._out.write_colour("#green#%d## tests passed, #red#%d## tests failed\n" % (passed, failed))
|
259
|
+
return False
|
260
|
+
else:
|
261
|
+
self._out.write_colour("#green#%d## tests passed, no failures\n" % passed)
|
262
|
+
return True
|
263
|
+
|
264
|
+
class OutProxy(object):
|
265
|
+
"""Proxy output class to make formatting easier.
|
266
|
+
|
267
|
+
Allows colourisation, and keeps track of whether we're mid-line or not.
|
268
|
+
|
269
|
+
"""
|
270
|
+
|
271
|
+
def __init__(self, out):
|
272
|
+
self._out = out
|
273
|
+
self._line_pos = 0 # Position on current line
|
274
|
+
self._had_space = True # True iff we're preceded by whitespace (including newline)
|
275
|
+
self.plain = not self._allow_control_sequences()
|
276
|
+
self._colours = self.get_colour_strings()
|
277
|
+
|
278
|
+
def _allow_control_sequences(self):
|
279
|
+
"Return True if output device allows control sequences."
|
280
|
+
mode = _os.environ.get("XAPIAN_TESTSUITE_OUTPUT", '').lower()
|
281
|
+
if mode in ('', 'auto'):
|
282
|
+
if _sys.platform == 'win32':
|
283
|
+
return False
|
284
|
+
elif not hasattr(self._out, "isatty"):
|
285
|
+
return False
|
286
|
+
else:
|
287
|
+
return self._out.isatty()
|
288
|
+
elif mode == 'plain':
|
289
|
+
return False
|
290
|
+
return True
|
291
|
+
|
292
|
+
def get_colour_strings(self):
|
293
|
+
"""Return a mapping of colour names to colour output sequences.
|
294
|
+
|
295
|
+
"""
|
296
|
+
colours = {
|
297
|
+
'red': "\x1b[1m\x1b[31m",
|
298
|
+
'green': "\x1b[1m\x1b[32m",
|
299
|
+
'yellow': "\x1b[1m\x1b[33m",
|
300
|
+
'': "\x1b[0m",
|
301
|
+
}
|
302
|
+
if self.plain:
|
303
|
+
for key in colours:
|
304
|
+
colours[key] = ''
|
305
|
+
return colours
|
306
|
+
|
307
|
+
def _colourise(self, msg):
|
308
|
+
"""Apply colours to a message.
|
309
|
+
|
310
|
+
#colourname# will change the text colour, ## will change the colour back.
|
311
|
+
|
312
|
+
"""
|
313
|
+
for colour, val in self._colours.iteritems():
|
314
|
+
msg = msg.replace('#%s#' % colour, val)
|
315
|
+
return msg
|
316
|
+
|
317
|
+
def clear_line(self):
|
318
|
+
"""Clear the current line of output, if possible.
|
319
|
+
|
320
|
+
Otherwise, just move to the start of the next line.
|
321
|
+
|
322
|
+
"""
|
323
|
+
if self._line_pos == 0:
|
324
|
+
return
|
325
|
+
if self.plain:
|
326
|
+
self.write('\n')
|
327
|
+
else:
|
328
|
+
self.write("\r" + " " * self._line_pos + "\r")
|
329
|
+
|
330
|
+
def start_line(self):
|
331
|
+
"""Ensure that we're at the start of a line.
|
332
|
+
|
333
|
+
"""
|
334
|
+
if self._line_pos != 0:
|
335
|
+
self.write('\n')
|
336
|
+
|
337
|
+
def ensure_space(self):
|
338
|
+
"""Ensure that we're preceded by whitespace.
|
339
|
+
|
340
|
+
"""
|
341
|
+
if not self._had_space:
|
342
|
+
self.write(' ')
|
343
|
+
|
344
|
+
def write(self, msg):
|
345
|
+
"""Write the message to the output stream.
|
346
|
+
|
347
|
+
"""
|
348
|
+
if len(msg) == 0:
|
349
|
+
return
|
350
|
+
|
351
|
+
# Adjust the line position counted
|
352
|
+
nlpos = max(msg.rfind('\n'), msg.rfind('\r'))
|
353
|
+
if nlpos >= 0:
|
354
|
+
subline = msg[nlpos + 1:]
|
355
|
+
self._line_pos = len(subline) # Note - doesn't cope with tabs.
|
356
|
+
else:
|
357
|
+
self._line_pos += len(msg) # Note - doesn't cope with tabs.
|
358
|
+
|
359
|
+
# Record whether we ended with whitespace
|
360
|
+
self._had_space = msg[-1].isspace()
|
361
|
+
|
362
|
+
self._out.write(msg)
|
363
|
+
|
364
|
+
def write_colour(self, msg):
|
365
|
+
"""Write a message, first substituting markup for colours.
|
366
|
+
|
367
|
+
"""
|
368
|
+
self.write(self._colourise(msg))
|
369
|
+
|
370
|
+
def flush(self):
|
371
|
+
self._out.flush()
|
372
|
+
|
373
|
+
|
374
|
+
_runner = TestRunner()
|
375
|
+
context = _runner.context
|
376
|
+
expect = _runner.expect
|
377
|
+
expect_query = _runner.expect_query
|
378
|
+
expect_exception = _runner.expect_exception
|
379
|
+
runtests = _runner.runtests
|
380
|
+
|
381
|
+
__all__ = ('context', 'expect', 'expect_query', 'expect_exception', 'runtests')
|
382
|
+
|