mhs-xapian 1.0.18a
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|