[ci] Fix clang-santisers job for GHA change
[xapian.git] / xapian-bindings / java / SmokeTest.java
blob8894e7811760318eb6c082088fb1a3316dc74ff3
1 // Simple test that we can use xapian from java
2 //
3 // Copyright (C) 2005,2006,2007,2008,2011,2016,2017,2019,2023 Olly Betts
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.
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
20 import org.xapian.*;
22 // FIXME: need to sort out throwing wrapped Xapian::Error subclasses
23 //import org.xapian.errors.*;
25 // FIXME: "implements" not "extends" in JNI Java API
26 class MyMatchDecider extends MatchDecider {
27 public boolean accept(Document d) {
28 // NB It's not normally appropriate to call getData() in a MatchDecider
29 // but we do it here to make sure we don't get an empty document.
30 /* try { */
31 return d.getData().length() == 0;
33 } catch (XapianError e) {
34 return true;
40 // FIXME: "implements" not "extends" in JNI Java API
41 class MyExpandDecider extends ExpandDecider {
42 public boolean accept(String s) { return s.charAt(0) != 'a'; }
45 class MyFieldProcessor extends FieldProcessor {
46 public Query apply(String str) {
47 if (str.equals("spam"))
48 return new Query("eggs");
49 return new Query("spam");
53 public class SmokeTest {
54 public static void main(String[] args) throws Exception {
55 TermGenerator termGenerator = new TermGenerator();
56 termGenerator.setFlags(TermGenerator.FLAG_SPELLING);
57 try {
58 // Test the version number reporting functions give plausible
59 // results.
60 String v = "";
61 v += Version.major();
62 v += ".";
63 v += Version.minor();
64 v += ".";
65 v += Version.revision();
66 String v2 = Version.string();
67 if (!v.equals(v2)) {
68 System.err.println("Unexpected version output (" + v + " != " + v2 + ")");
69 System.exit(1);
72 Stem stem = new Stem("english");
73 if (!stem.toString().equals("Xapian::Stem(english)")) {
74 System.err.println("Unexpected stem.toString()");
75 System.exit(1);
77 Document doc = new Document();
78 doc.setData("a\000b");
79 String s = doc.getData();
80 if (s.equals("a")) {
81 System.err.println("getData+setData truncates at a zero byte");
82 System.exit(1);
84 if (!s.equals("a\000b")) {
85 System.err.println("getData+setData doesn't transparently handle a zero byte");
86 System.exit(1);
88 if (!doc.toString().equals("Document(docid=0, data=a\\xc0\\x80b)")) {
89 System.err.println("XPASS: UTF-8 encoding of zero byte fixed!");
90 System.exit(1);
93 // Surrogate pair case:
94 String falafel = new String(Character.toChars(0x1f9c6));
95 doc.setData(falafel);
96 s = doc.getData();
97 if (!s.equals(falafel)) {
98 System.err.println("getData+setData doesn't transparently handle a surrogate pair");
99 System.exit(1);
101 if (!doc.toString().equals("Document(docid=0, data=\\xed\\xa0\\xbe\\xed\\xb7\\x86)")) {
102 System.err.println("XPASS: UTF-8 encoding of character >= U+10000 fixed!");
103 System.exit(1);
106 doc.setData("is there anybody out there?");
107 doc.addTerm("XYzzy");
108 // apply was stemWord() in the JNI bindings
109 doc.addPosting(stem.apply("is"), 1);
110 doc.addPosting(stem.apply("there"), 2);
111 doc.addPosting(stem.apply("anybody"), 3);
112 doc.addPosting(stem.apply("out"), 4);
113 doc.addPosting(stem.apply("there"), 5);
114 WritableDatabase db = new WritableDatabase("", Xapian.DB_BACKEND_INMEMORY);
115 db.addDocument(doc);
116 if (db.getDocCount() != 1) {
117 System.err.println("Unexpected db.getDocCount()");
118 System.exit(1);
121 QueryParser qp = new QueryParser();
123 // Test wrapping of null-able grouping parameter.
124 qp.addBooleanPrefix("colour", "XC");
125 qp.addBooleanPrefix("color", "XC");
126 qp.addBooleanPrefix("foo", "XFOO", null);
127 qp.addBooleanPrefix("bar", "XBAR", "XBA*");
128 qp.addBooleanPrefix("baa", "XBAA", "XBA*");
129 DateRangeProcessor rpdate = new DateRangeProcessor(1, Xapian.RP_DATE_PREFER_MDY, 1960);
130 qp.addRangeprocessor(rpdate);
131 qp.addRangeprocessor(rpdate, null);
132 qp.addRangeprocessor(rpdate, "foo");
134 if (!Query.MatchAll.toString().equals("Query(<alldocuments>)")) {
135 System.err.println("Unexpected Query.MatchAll.toString()");
136 System.exit(1);
139 if (!Query.MatchNothing.toString().equals("Query()")) {
140 System.err.println("Unexpected Query.MatchNothing.toString()");
141 System.exit(1);
144 String[] terms = { "smoke", "test", "terms" };
145 Query query = new Query(Query.OP_OR, terms);
146 if (!query.toString().equals("Query((smoke OR test OR terms))")) {
147 System.err.println("Unexpected query.toString()");
148 System.exit(1);
150 Query[] queries = { new Query("smoke"), query, new Query("string") };
151 Query query2 = new Query(Query.OP_XOR, queries);
152 if (!query2.toString().equals("Query((smoke XOR (smoke OR test OR terms) XOR string))")) {
153 System.err.println("Unexpected query2.toString()");
154 System.exit(1);
156 String[] subqs = { "a", "b" };
157 Query query3 = new Query(Query.OP_OR, subqs);
158 if (!query3.toString().equals("Query((a OR b))")) {
159 System.err.println("Unexpected query3.toString()");
160 System.exit(1);
162 Enquire enq = new Enquire(db);
164 // Check Xapian::BAD_VALUENO is wrapped suitably.
165 enq.setCollapseKey(Xapian.BAD_VALUENO);
167 // Test that the non-constant wrapping prior to 1.4.10 still works.
168 enq.setCollapseKey(Xapian.getBAD_VALUENO());
170 enq.setQuery(new Query(Query.OP_OR, "there", "is"));
171 MSet mset = enq.getMSet(0, 10);
172 if (mset.size() != 1) {
173 System.err.println("Unexpected mset.size()");
174 System.exit(1);
176 MSetIterator m_itor = mset.begin();
177 Document m_doc = null;
178 long m_id;
179 while(m_itor.hasNext()) {
180 m_id = m_itor.next();
181 if(m_itor.hasNext()) {
182 m_doc = mset.getDocument(m_id);
186 // Only one doc exists in this mset
187 if(m_doc != null && m_doc.getDocId() != 0) {
188 System.err.println("Unexpected docid");
189 System.exit(1);
192 String term_str = "";
193 TermIterator itor = enq.getMatchingTermsBegin(mset.getElement(0));
194 while (itor.hasNext()) {
195 term_str += itor.next();
196 if (itor.hasNext())
197 term_str += ' ';
199 if (!term_str.equals("is there")) {
200 System.err.println("Unexpected term_str");
201 System.exit(1);
203 /* FIXME:dc: Fails since Xapian::Error is still unmapped
204 boolean ok = false;
205 try {
206 Database db_fail = new Database("NOsuChdaTabASe");
207 // Ignore the return value.
208 db_fail.getDocCount();
209 } catch (DatabaseOpeningError e) {
210 ok = true;
212 if (!ok) {
213 System.err.println("Managed to open non-existent database");
214 System.exit(1);
218 if (Query.OP_ELITE_SET != 10) {
219 System.err.println("OP_ELITE_SET is " + Query.OP_ELITE_SET + " not 10");
220 System.exit(1);
223 RSet rset = new RSet();
224 rset.addDocument(1);
225 ESet eset = enq.getESet(10, rset, new MyExpandDecider());
226 // FIXME: temporary simple check
227 if (0 == eset.size()) {
228 System.err.println("ESet.size() was 0");
229 System.exit(1);
232 int count = 0;
233 for(ESetIterator eit = eset.begin(); eit.hasNext(); ) {
234 // for (int i = 0; i < eset.size(); i++) {
235 if (eit.getTerm().charAt(0) == 'a') {
236 System.err.println("MyExpandDecider wasn't used");
237 System.exit(1);
239 ++count;
240 eit.next();
242 if (count != eset.size()) {
243 System.err.println("ESet.size() mismatched number of terms returned by ESetIterator");
244 System.err.println(count + " " + eset.size());
245 System.exit(1);
249 MSet mset2 = enq.getMSet(0, 10, null, new MyMatchDecider());
250 if (mset2.size() > 0) {
251 System.err.println("MyMatchDecider wasn't used");
252 System.exit(1);
256 if (!enq.getQuery().toString().equals("Query((there OR is))")) {
257 System.err.println("Enquire::getQuery() returned the wrong query: " + enq.getQuery().toString());
258 System.exit(1);
262 qp.addPrefix("food", new MyFieldProcessor());
263 if (!qp.parseQuery("food:spam").toString().equals("Query(eggs)")) {
264 System.err.println("FieldProcessor subclass doesn't work as expected");
265 System.exit(1);
270 // Wrapped functions which take/return byte[] for std::string.
272 // Check that serialisation returns byte[], that
273 // unserialisation takes byte[], and round-tripping works.
274 byte[] res = Xapian.sortableSerialise(1.675);
275 if (Xapian.sortableUnserialise(res) != 1.675) {
276 System.err.println("sortableSerialise() and/or sortableUnserialise() don't work as expected");
277 System.exit(1);
280 // Check that serialisation returns byte[], that
281 // unserialisation takes byte[], and round-tripping works.
282 Query q = new Query("foo");
283 res = q.serialise();
284 Query q_out = Query.unserialise(res);
285 if (!q.toString().equals(q_out.toString())) {
286 System.err.println("Query serialisation doesn't work as expected");
287 System.exit(1);
290 // Check Document.addValue() takes byte[], that getValue()
291 // returns byte[], that serialisation returns byte[], that
292 // unserialisation takes byte[], and round-tripping works.
293 Document d = new Document();
294 d.setData("xyzzy");
295 d.addValue(7, res);
296 byte[] res2 = d.getValue(7);
297 if (!java.util.Arrays.equals(res, res2)) {
298 System.err.println("Document.getValue() returns a different byte[] to the one set with addValue()");
299 System.exit(1);
301 res = d.serialise();
302 Document d_out = Document.unserialise(res);
303 // Make sure the "terms_here" flag is set so the descriptions match.
304 d_out.termListCount();
305 if (!d.toString().equals(d_out.toString())) {
306 System.err.println("Document serialisation doesn't work as expected");
307 System.err.println(d.toString());
308 System.err.println(d_out.toString());
309 System.exit(1);
312 // Check that serialisation returns byte[], that
313 // unserialisation takes byte[], and round-tripping works.
314 LatLongCoord llc = new LatLongCoord(10.5, 45.25);
315 res = llc.serialise();
316 LatLongCoord llc_out = new LatLongCoord();
317 llc_out.unserialise(res);
318 if (!llc.toString().equals(llc_out.toString())) {
319 System.err.println("LatLongCoord serialisation doesn't work as expected");
320 System.exit(1);
323 // Check that serialisation returns byte[], that
324 // unserialisation takes byte[], and round-tripping works.
325 LatLongCoords llcs = new LatLongCoords();
326 llcs.append(llc);
327 res = llcs.serialise();
328 LatLongCoords llcs_out = new LatLongCoords();
329 llcs_out.unserialise(res);
330 if (!llcs.toString().equals(llcs_out.toString())) {
331 System.err.println("LatLongCoords serialisation doesn't work as expected");
332 System.exit(1);
335 // Check `range_limit` mapped to byte[].
336 q = new Query(Query.op.OP_VALUE_GE, 0, res);
337 if (q.toString().length() == 0) {
338 // Mostly just a way to actually use the constructed object.
339 System.err.println("Query description shouldn't be empty");
340 System.exit(1);
343 // Check `range_limit` mapped to byte[].
344 q = new Query(Query.op.OP_VALUE_LE, 1, res);
345 if (q.toString().length() == 0) {
346 // Mostly just a way to actually use the constructed object.
347 System.err.println("Query description shouldn't be empty");
348 System.exit(1);
351 // Check `range_lower` and `range_upper` mapped to byte[].
352 q = new Query(Query.op.OP_VALUE_RANGE, 2, res, res);
353 if (q.toString().length() == 0) {
354 // Mostly just a way to actually use the constructed object.
355 System.err.println("Query description shouldn't be empty");
356 System.exit(1);
359 // Check ValueSetMatchDecider.addValue() and removeValue() take
360 // byte[].
361 ValueSetMatchDecider vsmd = new ValueSetMatchDecider(1, false);
362 vsmd.addValue(res);
363 vsmd.removeValue(res);
365 // Check Database.getValueLowerBound() and getValueUpperBound()
366 // return byte[].
367 byte[] lo = "abba".getBytes();
368 byte[] hi = "xyzzy".getBytes();
370 WritableDatabase wdb = new WritableDatabase("", Xapian.DB_BACKEND_INMEMORY);
371 Document document = new Document();
372 document.addValue(42, hi);
373 wdb.addDocument(document);
374 document.addValue(42, lo);
375 wdb.addDocument(document);
376 db = wdb;
379 if (!java.util.Arrays.equals(db.getValueLowerBound(42), lo)) {
380 System.err.println("Database.getValueLowerBound() doesn't work as expected");
381 System.exit(1);
383 if (!java.util.Arrays.equals(db.getValueUpperBound(42), hi)) {
384 System.err.println("Database.getValueUpperBound() doesn't work as expected");
385 System.exit(1);
388 ValueIterator it = db.valuestreamBegin(42);
389 if (!java.util.Arrays.equals(it.getValue(), hi)) {
390 System.err.println("ValueIterator.getValue() doesn't work as expected");
391 System.exit(1);
394 } catch (Exception e) {
395 System.err.println("Caught unexpected exception " + e.toString());
396 System.exit(1);