• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.google.android.icing;
16 
17 import static com.google.common.truth.Truth.assertThat;
18 import static com.google.common.truth.Truth.assertWithMessage;
19 
20 import com.google.android.icing.IcingSearchEngine;
21 import com.google.android.icing.proto.DebugInfoResultProto;
22 import com.google.android.icing.proto.DebugInfoVerbosity;
23 import com.google.android.icing.proto.DeleteByNamespaceResultProto;
24 import com.google.android.icing.proto.DeleteByQueryResultProto;
25 import com.google.android.icing.proto.DeleteBySchemaTypeResultProto;
26 import com.google.android.icing.proto.DeleteResultProto;
27 import com.google.android.icing.proto.DocumentProto;
28 import com.google.android.icing.proto.GetAllNamespacesResultProto;
29 import com.google.android.icing.proto.GetOptimizeInfoResultProto;
30 import com.google.android.icing.proto.GetResultProto;
31 import com.google.android.icing.proto.GetResultSpecProto;
32 import com.google.android.icing.proto.GetSchemaResultProto;
33 import com.google.android.icing.proto.GetSchemaTypeResultProto;
34 import com.google.android.icing.proto.IcingSearchEngineOptions;
35 import com.google.android.icing.proto.InitializeResultProto;
36 import com.google.android.icing.proto.LogSeverity;
37 import com.google.android.icing.proto.OptimizeResultProto;
38 import com.google.android.icing.proto.PersistToDiskResultProto;
39 import com.google.android.icing.proto.PersistType;
40 import com.google.android.icing.proto.PropertyConfigProto;
41 import com.google.android.icing.proto.PropertyProto;
42 import com.google.android.icing.proto.PutResultProto;
43 import com.google.android.icing.proto.ReportUsageResultProto;
44 import com.google.android.icing.proto.ResetResultProto;
45 import com.google.android.icing.proto.ResultSpecProto;
46 import com.google.android.icing.proto.SchemaProto;
47 import com.google.android.icing.proto.SchemaTypeConfigProto;
48 import com.google.android.icing.proto.ScoringSpecProto;
49 import com.google.android.icing.proto.SearchResultProto;
50 import com.google.android.icing.proto.SearchSpecProto;
51 import com.google.android.icing.proto.SetSchemaResultProto;
52 import com.google.android.icing.proto.SnippetMatchProto;
53 import com.google.android.icing.proto.SnippetProto;
54 import com.google.android.icing.proto.StatusProto;
55 import com.google.android.icing.proto.StorageInfoResultProto;
56 import com.google.android.icing.proto.StringIndexingConfig;
57 import com.google.android.icing.proto.StringIndexingConfig.TokenizerType;
58 import com.google.android.icing.proto.SuggestionResponse;
59 import com.google.android.icing.proto.SuggestionScoringSpecProto;
60 import com.google.android.icing.proto.SuggestionSpecProto;
61 import com.google.android.icing.proto.TermMatchType;
62 import com.google.android.icing.proto.TermMatchType.Code;
63 import com.google.android.icing.proto.UsageReport;
64 import java.io.File;
65 import java.util.HashMap;
66 import java.util.Map;
67 import org.junit.After;
68 import org.junit.Before;
69 import org.junit.Rule;
70 import org.junit.Test;
71 import org.junit.rules.TemporaryFolder;
72 import org.junit.runner.RunWith;
73 import org.junit.runners.JUnit4;
74 
75 /**
76  * This test is not intended to fully test the functionality of each API. But rather to test the JNI
77  * wrapper and Java interfaces of Icing library {@link IcingSearchEngine}.
78  */
79 @RunWith(JUnit4.class)
80 public final class IcingSearchEngineTest {
81   @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
82 
83   private static final String EMAIL_TYPE = "Email";
84 
85   private File tempDir;
86 
87   private IcingSearchEngine icingSearchEngine;
88 
createEmailTypeConfig()89   private static SchemaTypeConfigProto createEmailTypeConfig() {
90     return SchemaTypeConfigProto.newBuilder()
91         .setSchemaType(EMAIL_TYPE)
92         .addProperties(
93             PropertyConfigProto.newBuilder()
94                 .setPropertyName("subject")
95                 .setDataType(PropertyConfigProto.DataType.Code.STRING)
96                 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
97                 .setStringIndexingConfig(
98                     StringIndexingConfig.newBuilder()
99                         .setTokenizerType(TokenizerType.Code.PLAIN)
100                         .setTermMatchType(TermMatchType.Code.PREFIX)))
101         .addProperties(
102             PropertyConfigProto.newBuilder()
103                 .setPropertyName("body")
104                 .setDataType(PropertyConfigProto.DataType.Code.STRING)
105                 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
106                 .setStringIndexingConfig(
107                     StringIndexingConfig.newBuilder()
108                         .setTokenizerType(TokenizerType.Code.PLAIN)
109                         .setTermMatchType(TermMatchType.Code.PREFIX)))
110         .build();
111   }
112 
createEmailDocument(String namespace, String uri)113   private static DocumentProto createEmailDocument(String namespace, String uri) {
114     return DocumentProto.newBuilder()
115         .setNamespace(namespace)
116         .setUri(uri)
117         .setSchema(EMAIL_TYPE)
118         .setCreationTimestampMs(1) // Arbitrary non-zero number so Icing doesn't override it
119         .build();
120   }
121 
122   @Before
setUp()123   public void setUp() throws Exception {
124     tempDir = temporaryFolder.newFolder();
125     IcingSearchEngineOptions options =
126         IcingSearchEngineOptions.newBuilder().setBaseDir(tempDir.getCanonicalPath()).build();
127     icingSearchEngine = new IcingSearchEngine(options);
128   }
129 
130   @After
tearDown()131   public void tearDown() throws Exception {
132     icingSearchEngine.close();
133   }
134 
135   @Test
testInitialize()136   public void testInitialize() throws Exception {
137     InitializeResultProto initializeResultProto = icingSearchEngine.initialize();
138     assertStatusOk(initializeResultProto.getStatus());
139   }
140 
141   @Test
testSetAndGetSchema()142   public void testSetAndGetSchema() throws Exception {
143     assertStatusOk(icingSearchEngine.initialize().getStatus());
144 
145     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
146     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
147     SetSchemaResultProto setSchemaResultProto =
148         icingSearchEngine.setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false);
149     assertStatusOk(setSchemaResultProto.getStatus());
150 
151     GetSchemaResultProto getSchemaResultProto = icingSearchEngine.getSchema();
152     assertStatusOk(getSchemaResultProto.getStatus());
153     assertThat(getSchemaResultProto.getSchema()).isEqualTo(schema);
154 
155     GetSchemaTypeResultProto getSchemaTypeResultProto =
156         icingSearchEngine.getSchemaType(emailTypeConfig.getSchemaType());
157     assertStatusOk(getSchemaTypeResultProto.getStatus());
158     assertThat(getSchemaTypeResultProto.getSchemaTypeConfig()).isEqualTo(emailTypeConfig);
159   }
160 
161   @Test
testPutAndGetDocuments()162   public void testPutAndGetDocuments() throws Exception {
163     assertStatusOk(icingSearchEngine.initialize().getStatus());
164 
165     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
166     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
167     assertThat(
168             icingSearchEngine
169                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
170                 .getStatus()
171                 .getCode())
172         .isEqualTo(StatusProto.Code.OK);
173 
174     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
175     PutResultProto putResultProto = icingSearchEngine.put(emailDocument);
176     assertStatusOk(putResultProto.getStatus());
177 
178     GetResultProto getResultProto =
179         icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance());
180     assertStatusOk(getResultProto.getStatus());
181     assertThat(getResultProto.getDocument()).isEqualTo(emailDocument);
182   }
183 
184   @Test
testSearch()185   public void testSearch() throws Exception {
186     assertStatusOk(icingSearchEngine.initialize().getStatus());
187 
188     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
189     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
190     assertThat(
191             icingSearchEngine
192                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
193                 .getStatus()
194                 .getCode())
195         .isEqualTo(StatusProto.Code.OK);
196 
197     DocumentProto emailDocument =
198         createEmailDocument("namespace", "uri").toBuilder()
199             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
200             .build();
201     assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
202 
203     SearchSpecProto searchSpec =
204         SearchSpecProto.newBuilder()
205             .setQuery("foo")
206             .setTermMatchType(TermMatchType.Code.PREFIX)
207             .build();
208 
209     SearchResultProto searchResultProto =
210         icingSearchEngine.search(
211             searchSpec,
212             ScoringSpecProto.getDefaultInstance(),
213             ResultSpecProto.getDefaultInstance());
214     assertStatusOk(searchResultProto.getStatus());
215     assertThat(searchResultProto.getResultsCount()).isEqualTo(1);
216     assertThat(searchResultProto.getResults(0).getDocument()).isEqualTo(emailDocument);
217 
218     assertThat(searchResultProto.getQueryStats().hasNativeToJavaStartTimestampMs()).isTrue();
219     assertThat(searchResultProto.getQueryStats().hasNativeToJavaJniLatencyMs()).isTrue();
220     assertThat(searchResultProto.getQueryStats().hasJavaToNativeJniLatencyMs()).isTrue();
221     assertThat(searchResultProto.getQueryStats().getNativeToJavaStartTimestampMs())
222         .isGreaterThan(0);
223     assertThat(searchResultProto.getQueryStats().getNativeToJavaJniLatencyMs()).isAtLeast(0);
224     assertThat(searchResultProto.getQueryStats().getJavaToNativeJniLatencyMs()).isAtLeast(0);
225   }
226 
227   @Test
testGetNextPage()228   public void testGetNextPage() throws Exception {
229     assertStatusOk(icingSearchEngine.initialize().getStatus());
230 
231     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
232     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
233     assertThat(
234             icingSearchEngine
235                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
236                 .getStatus()
237                 .getCode())
238         .isEqualTo(StatusProto.Code.OK);
239 
240     Map<String, DocumentProto> documents = new HashMap<>();
241     for (int i = 0; i < 10; i++) {
242       DocumentProto emailDocument =
243           createEmailDocument("namespace", "uri:" + i).toBuilder()
244               .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
245               .build();
246       documents.put("uri:" + i, emailDocument);
247       assertWithMessage(icingSearchEngine.put(emailDocument).getStatus().getMessage())
248           .that(icingSearchEngine.put(emailDocument).getStatus().getCode())
249           .isEqualTo(StatusProto.Code.OK);
250     }
251 
252     SearchSpecProto searchSpec =
253         SearchSpecProto.newBuilder()
254             .setQuery("foo")
255             .setTermMatchType(TermMatchType.Code.PREFIX)
256             .build();
257     ResultSpecProto resultSpecProto = ResultSpecProto.newBuilder().setNumPerPage(1).build();
258 
259     SearchResultProto searchResultProto =
260         icingSearchEngine.search(
261             searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto);
262     assertStatusOk(searchResultProto.getStatus());
263     assertThat(searchResultProto.getResultsCount()).isEqualTo(1);
264     DocumentProto resultDocument = searchResultProto.getResults(0).getDocument();
265     assertThat(resultDocument).isEqualTo(documents.remove(resultDocument.getUri()));
266 
267     assertThat(searchResultProto.getQueryStats().hasNativeToJavaStartTimestampMs()).isTrue();
268     assertThat(searchResultProto.getQueryStats().hasNativeToJavaJniLatencyMs()).isTrue();
269     assertThat(searchResultProto.getQueryStats().hasJavaToNativeJniLatencyMs()).isTrue();
270     assertThat(searchResultProto.getQueryStats().getNativeToJavaStartTimestampMs())
271         .isGreaterThan(0);
272     assertThat(searchResultProto.getQueryStats().getNativeToJavaJniLatencyMs()).isAtLeast(0);
273     assertThat(searchResultProto.getQueryStats().getJavaToNativeJniLatencyMs()).isAtLeast(0);
274 
275     // fetch rest pages
276     for (int i = 1; i < 5; i++) {
277       searchResultProto = icingSearchEngine.getNextPage(searchResultProto.getNextPageToken());
278       assertWithMessage(searchResultProto.getStatus().getMessage())
279           .that(searchResultProto.getStatus().getCode())
280           .isEqualTo(StatusProto.Code.OK);
281       assertThat(searchResultProto.getResultsCount()).isEqualTo(1);
282       resultDocument = searchResultProto.getResults(0).getDocument();
283       assertThat(resultDocument).isEqualTo(documents.remove(resultDocument.getUri()));
284     }
285 
286     // invalidate rest result
287     icingSearchEngine.invalidateNextPageToken(searchResultProto.getNextPageToken());
288 
289     searchResultProto = icingSearchEngine.getNextPage(searchResultProto.getNextPageToken());
290     assertStatusOk(searchResultProto.getStatus());
291     assertThat(searchResultProto.getResultsCount()).isEqualTo(0);
292   }
293 
294   @Test
testDelete()295   public void testDelete() throws Exception {
296     assertStatusOk(icingSearchEngine.initialize().getStatus());
297 
298     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
299     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
300     assertThat(
301             icingSearchEngine
302                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
303                 .getStatus()
304                 .getCode())
305         .isEqualTo(StatusProto.Code.OK);
306 
307     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
308     assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
309 
310     DeleteResultProto deleteResultProto = icingSearchEngine.delete("namespace", "uri");
311     assertStatusOk(deleteResultProto.getStatus());
312 
313     GetResultProto getResultProto =
314         icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance());
315     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
316   }
317 
318   @Test
testDeleteByNamespace()319   public void testDeleteByNamespace() throws Exception {
320     assertStatusOk(icingSearchEngine.initialize().getStatus());
321 
322     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
323     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
324     assertThat(
325             icingSearchEngine
326                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
327                 .getStatus()
328                 .getCode())
329         .isEqualTo(StatusProto.Code.OK);
330 
331     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
332     assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
333 
334     DeleteByNamespaceResultProto deleteByNamespaceResultProto =
335         icingSearchEngine.deleteByNamespace("namespace");
336     assertStatusOk(deleteByNamespaceResultProto.getStatus());
337 
338     GetResultProto getResultProto =
339         icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance());
340     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
341   }
342 
343   @Test
testDeleteBySchemaType()344   public void testDeleteBySchemaType() throws Exception {
345     assertStatusOk(icingSearchEngine.initialize().getStatus());
346 
347     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
348     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
349     assertThat(
350             icingSearchEngine
351                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
352                 .getStatus()
353                 .getCode())
354         .isEqualTo(StatusProto.Code.OK);
355 
356     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
357     assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
358 
359     DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto =
360         icingSearchEngine.deleteBySchemaType(EMAIL_TYPE);
361     assertStatusOk(deleteBySchemaTypeResultProto.getStatus());
362 
363     GetResultProto getResultProto =
364         icingSearchEngine.get("namespace", "uri", GetResultSpecProto.getDefaultInstance());
365     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
366   }
367 
368   @Test
testDeleteByQuery()369   public void testDeleteByQuery() throws Exception {
370     assertStatusOk(icingSearchEngine.initialize().getStatus());
371 
372     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
373     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
374     assertThat(
375             icingSearchEngine
376                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
377                 .getStatus()
378                 .getCode())
379         .isEqualTo(StatusProto.Code.OK);
380 
381     DocumentProto emailDocument1 =
382         createEmailDocument("namespace", "uri1").toBuilder()
383             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
384             .build();
385 
386     assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus());
387     DocumentProto emailDocument2 =
388         createEmailDocument("namespace", "uri2").toBuilder()
389             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("bar"))
390             .build();
391 
392     assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus());
393 
394     SearchSpecProto searchSpec =
395         SearchSpecProto.newBuilder()
396             .setQuery("foo")
397             .setTermMatchType(TermMatchType.Code.PREFIX)
398             .build();
399 
400     SearchResultProto searchResultProto =
401         icingSearchEngine.search(
402             searchSpec,
403             ScoringSpecProto.getDefaultInstance(),
404             ResultSpecProto.getDefaultInstance());
405     assertStatusOk(searchResultProto.getStatus());
406     assertThat(searchResultProto.getResultsCount()).isEqualTo(1);
407     assertThat(searchResultProto.getResults(0).getDocument()).isEqualTo(emailDocument1);
408 
409     DeleteByQueryResultProto deleteResultProto = icingSearchEngine.deleteByQuery(searchSpec);
410     assertStatusOk(deleteResultProto.getStatus());
411     // By default, the deleteByQuery API does not return the summary about deleted documents, unless
412     // the returnDeletedDocumentInfo parameter is set to true.
413     assertThat(deleteResultProto.getDeletedDocumentsList()).isEmpty();
414 
415     GetResultProto getResultProto =
416         icingSearchEngine.get("namespace", "uri1", GetResultSpecProto.getDefaultInstance());
417     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
418     getResultProto =
419         icingSearchEngine.get("namespace", "uri2", GetResultSpecProto.getDefaultInstance());
420     assertStatusOk(getResultProto.getStatus());
421   }
422 
423   @Test
testDeleteByQueryWithDeletedDocumentInfo()424   public void testDeleteByQueryWithDeletedDocumentInfo() throws Exception {
425     assertStatusOk(icingSearchEngine.initialize().getStatus());
426 
427     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
428     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
429     assertThat(
430             icingSearchEngine
431                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
432                 .getStatus()
433                 .getCode())
434         .isEqualTo(StatusProto.Code.OK);
435 
436     DocumentProto emailDocument1 =
437         createEmailDocument("namespace", "uri1").toBuilder()
438             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
439             .build();
440 
441     assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus());
442     DocumentProto emailDocument2 =
443         createEmailDocument("namespace", "uri2").toBuilder()
444             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("bar"))
445             .build();
446 
447     assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus());
448 
449     SearchSpecProto searchSpec =
450         SearchSpecProto.newBuilder()
451             .setQuery("foo")
452             .setTermMatchType(TermMatchType.Code.PREFIX)
453             .build();
454 
455     DeleteByQueryResultProto deleteResultProto =
456         icingSearchEngine.deleteByQuery(searchSpec, /*returnDeletedDocumentInfo=*/ true);
457     assertStatusOk(deleteResultProto.getStatus());
458     DeleteByQueryResultProto.DocumentGroupInfo info =
459         DeleteByQueryResultProto.DocumentGroupInfo.newBuilder()
460             .setNamespace("namespace")
461             .setSchema("Email")
462             .addUris("uri1")
463             .build();
464     assertThat(deleteResultProto.getDeletedDocumentsList()).containsExactly(info);
465 
466     GetResultProto getResultProto =
467         icingSearchEngine.get("namespace", "uri1", GetResultSpecProto.getDefaultInstance());
468     assertThat(getResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.NOT_FOUND);
469     getResultProto =
470         icingSearchEngine.get("namespace", "uri2", GetResultSpecProto.getDefaultInstance());
471     assertStatusOk(getResultProto.getStatus());
472   }
473 
474   @Test
testPersistToDisk()475   public void testPersistToDisk() throws Exception {
476     assertStatusOk(icingSearchEngine.initialize().getStatus());
477 
478     PersistToDiskResultProto persistToDiskResultProto =
479         icingSearchEngine.persistToDisk(PersistType.Code.LITE);
480     assertStatusOk(persistToDiskResultProto.getStatus());
481   }
482 
483   @Test
testOptimize()484   public void testOptimize() throws Exception {
485     assertStatusOk(icingSearchEngine.initialize().getStatus());
486 
487     OptimizeResultProto optimizeResultProto = icingSearchEngine.optimize();
488     assertStatusOk(optimizeResultProto.getStatus());
489   }
490 
491   @Test
testGetOptimizeInfo()492   public void testGetOptimizeInfo() throws Exception {
493     assertStatusOk(icingSearchEngine.initialize().getStatus());
494 
495     GetOptimizeInfoResultProto getOptimizeInfoResultProto = icingSearchEngine.getOptimizeInfo();
496     assertStatusOk(getOptimizeInfoResultProto.getStatus());
497     assertThat(getOptimizeInfoResultProto.getOptimizableDocs()).isEqualTo(0);
498     assertThat(getOptimizeInfoResultProto.getEstimatedOptimizableBytes()).isEqualTo(0);
499   }
500 
501   @Test
testGetStorageInfo()502   public void testGetStorageInfo() throws Exception {
503     assertStatusOk(icingSearchEngine.initialize().getStatus());
504 
505     StorageInfoResultProto storageInfoResultProto = icingSearchEngine.getStorageInfo();
506     assertStatusOk(storageInfoResultProto.getStatus());
507   }
508 
509   @Test
testGetDebugInfo()510   public void testGetDebugInfo() throws Exception {
511     assertStatusOk(icingSearchEngine.initialize().getStatus());
512 
513     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
514     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
515     assertThat(
516             icingSearchEngine
517                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
518                 .getStatus()
519                 .getCode())
520         .isEqualTo(StatusProto.Code.OK);
521 
522     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
523     assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
524 
525     DebugInfoResultProto debugInfoResultProtoBasic =
526         icingSearchEngine.getDebugInfo(DebugInfoVerbosity.Code.BASIC);
527     assertStatusOk(debugInfoResultProtoBasic.getStatus());
528     assertThat(debugInfoResultProtoBasic.getDebugInfo().getDocumentInfo().getCorpusInfoList())
529         .isEmpty(); // because verbosity=BASIC
530 
531     DebugInfoResultProto debugInfoResultProtoDetailed =
532         icingSearchEngine.getDebugInfo(DebugInfoVerbosity.Code.DETAILED);
533     assertStatusOk(debugInfoResultProtoDetailed.getStatus());
534     assertThat(debugInfoResultProtoDetailed.getDebugInfo().getDocumentInfo().getCorpusInfoList())
535         .hasSize(1); // because verbosity=DETAILED
536   }
537 
538   @Test
testGetAllNamespaces()539   public void testGetAllNamespaces() throws Exception {
540     assertStatusOk(icingSearchEngine.initialize().getStatus());
541 
542     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
543     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
544     assertThat(
545             icingSearchEngine
546                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
547                 .getStatus()
548                 .getCode())
549         .isEqualTo(StatusProto.Code.OK);
550 
551     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
552     assertStatusOk(icingSearchEngine.put(emailDocument).getStatus());
553 
554     GetAllNamespacesResultProto getAllNamespacesResultProto = icingSearchEngine.getAllNamespaces();
555     assertStatusOk(getAllNamespacesResultProto.getStatus());
556     assertThat(getAllNamespacesResultProto.getNamespacesList()).containsExactly("namespace");
557   }
558 
559   @Test
testReset()560   public void testReset() throws Exception {
561     assertStatusOk(icingSearchEngine.initialize().getStatus());
562 
563     ResetResultProto resetResultProto = icingSearchEngine.reset();
564     assertStatusOk(resetResultProto.getStatus());
565   }
566 
567   @Test
testReportUsage()568   public void testReportUsage() throws Exception {
569     assertStatusOk(icingSearchEngine.initialize().getStatus());
570 
571     // Set schema and put a document.
572     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
573     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
574     assertThat(
575             icingSearchEngine
576                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
577                 .getStatus()
578                 .getCode())
579         .isEqualTo(StatusProto.Code.OK);
580 
581     DocumentProto emailDocument = createEmailDocument("namespace", "uri");
582     PutResultProto putResultProto = icingSearchEngine.put(emailDocument);
583     assertStatusOk(putResultProto.getStatus());
584 
585     // Report usage
586     UsageReport usageReport =
587         UsageReport.newBuilder()
588             .setDocumentNamespace("namespace")
589             .setDocumentUri("uri")
590             .setUsageTimestampMs(1)
591             .setUsageType(UsageReport.UsageType.USAGE_TYPE1)
592             .build();
593     ReportUsageResultProto reportUsageResultProto = icingSearchEngine.reportUsage(usageReport);
594     assertStatusOk(reportUsageResultProto.getStatus());
595   }
596 
597   @Test
testCJKTSnippets()598   public void testCJKTSnippets() throws Exception {
599     assertStatusOk(icingSearchEngine.initialize().getStatus());
600 
601     SchemaProto schema = SchemaProto.newBuilder().addTypes(createEmailTypeConfig()).build();
602     assertStatusOk(
603         icingSearchEngine.setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false).getStatus());
604 
605     // String:     "天是蓝的"
606     //              ^ ^^ ^
607     // UTF16 idx:   0 1 2 3
608     // Breaks into segments: "天", "是", "蓝", "的"
609     // "The sky is blue"
610     String chinese = "天是蓝的";
611     assertThat(chinese.length()).isEqualTo(4);
612     DocumentProto emailDocument1 =
613         createEmailDocument("namespace", "uri1").toBuilder()
614             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues(chinese))
615             .build();
616     assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus());
617 
618     // Search and request snippet matching but no windowing.
619     SearchSpecProto searchSpec =
620         SearchSpecProto.newBuilder()
621             .setQuery("是")
622             .setTermMatchType(TermMatchType.Code.PREFIX)
623             .build();
624     ResultSpecProto resultSpecProto =
625         ResultSpecProto.newBuilder()
626             .setSnippetSpec(
627                 ResultSpecProto.SnippetSpecProto.newBuilder()
628                     .setNumToSnippet(Integer.MAX_VALUE)
629                     .setNumMatchesPerProperty(Integer.MAX_VALUE))
630             .build();
631 
632     // Search and make sure that we got a single successful results
633     SearchResultProto searchResultProto =
634         icingSearchEngine.search(
635             searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto);
636     assertStatusOk(searchResultProto.getStatus());
637     assertThat(searchResultProto.getResultsCount()).isEqualTo(1);
638 
639     // Ensure that one and only one property was matched and it was "subject"
640     SnippetProto snippetProto = searchResultProto.getResults(0).getSnippet();
641     assertThat(snippetProto.getEntriesList()).hasSize(1);
642     SnippetProto.EntryProto entryProto = snippetProto.getEntries(0);
643     assertThat(entryProto.getPropertyName()).isEqualTo("subject");
644 
645     // Get the content for "subject" and see what the match is.
646     DocumentProto resultDocument = searchResultProto.getResults(0).getDocument();
647     assertThat(resultDocument.getPropertiesList()).hasSize(1);
648     PropertyProto subjectProperty = resultDocument.getProperties(0);
649     assertThat(subjectProperty.getName()).isEqualTo("subject");
650     assertThat(subjectProperty.getStringValuesList()).hasSize(1);
651     String content = subjectProperty.getStringValues(0);
652 
653     // Ensure that there is one and only one match within "subject"
654     assertThat(entryProto.getSnippetMatchesList()).hasSize(1);
655     SnippetMatchProto matchProto = entryProto.getSnippetMatches(0);
656 
657     int matchStart = matchProto.getExactMatchUtf16Position();
658     int matchEnd = matchStart + matchProto.getExactMatchUtf16Length();
659     assertThat(matchStart).isEqualTo(1);
660     assertThat(matchEnd).isEqualTo(2);
661     String match = content.substring(matchStart, matchEnd);
662     assertThat(match).isEqualTo("是");
663   }
664 
665   @Test
testUtf16MultiByteSnippets()666   public void testUtf16MultiByteSnippets() throws Exception {
667     assertStatusOk(icingSearchEngine.initialize().getStatus());
668 
669     SchemaProto schema = SchemaProto.newBuilder().addTypes(createEmailTypeConfig()).build();
670     assertStatusOk(
671         icingSearchEngine.setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false).getStatus());
672 
673     // String:    "���� ���� ��"
674     //             ^  ^  ^
675     // UTF16 idx:  0  5  10
676     // Breaks into segments: "����", "����", "��"
677     String text = "���� ���� ��";
678     assertThat(text.length()).isEqualTo(12);
679     DocumentProto emailDocument1 =
680         createEmailDocument("namespace", "uri1").toBuilder()
681             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues(text))
682             .build();
683     assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus());
684 
685     // Search and request snippet matching but no windowing.
686     SearchSpecProto searchSpec =
687         SearchSpecProto.newBuilder()
688             .setQuery("��")
689             .setTermMatchType(TermMatchType.Code.PREFIX)
690             .build();
691     ResultSpecProto resultSpecProto =
692         ResultSpecProto.newBuilder()
693             .setSnippetSpec(
694                 ResultSpecProto.SnippetSpecProto.newBuilder()
695                     .setNumToSnippet(Integer.MAX_VALUE)
696                     .setNumMatchesPerProperty(Integer.MAX_VALUE))
697             .build();
698 
699     // Search and make sure that we got a single successful results
700     SearchResultProto searchResultProto =
701         icingSearchEngine.search(
702             searchSpec, ScoringSpecProto.getDefaultInstance(), resultSpecProto);
703     assertStatusOk(searchResultProto.getStatus());
704     assertThat(searchResultProto.getResultsCount()).isEqualTo(1);
705 
706     // Ensure that one and only one property was matched and it was "subject"
707     SnippetProto snippetProto = searchResultProto.getResults(0).getSnippet();
708     assertThat(snippetProto.getEntriesList()).hasSize(1);
709     SnippetProto.EntryProto entryProto = snippetProto.getEntries(0);
710     assertThat(entryProto.getPropertyName()).isEqualTo("subject");
711 
712     // Get the content for "subject" and see what the match is.
713     DocumentProto resultDocument = searchResultProto.getResults(0).getDocument();
714     assertThat(resultDocument.getPropertiesList()).hasSize(1);
715     PropertyProto subjectProperty = resultDocument.getProperties(0);
716     assertThat(subjectProperty.getName()).isEqualTo("subject");
717     assertThat(subjectProperty.getStringValuesList()).hasSize(1);
718     String content = subjectProperty.getStringValues(0);
719 
720     // Ensure that there is one and only one match within "subject"
721     assertThat(entryProto.getSnippetMatchesList()).hasSize(1);
722     SnippetMatchProto matchProto = entryProto.getSnippetMatches(0);
723 
724     int matchStart = matchProto.getExactMatchUtf16Position();
725     int matchEnd = matchStart + matchProto.getExactMatchUtf16Length();
726     assertThat(matchStart).isEqualTo(5);
727     assertThat(matchEnd).isEqualTo(9);
728     String match = content.substring(matchStart, matchEnd);
729     assertThat(match).isEqualTo("����");
730   }
731 
732   @Test
testSearchSuggestions()733   public void testSearchSuggestions() {
734     assertStatusOk(icingSearchEngine.initialize().getStatus());
735 
736     SchemaTypeConfigProto emailTypeConfig = createEmailTypeConfig();
737     SchemaProto schema = SchemaProto.newBuilder().addTypes(emailTypeConfig).build();
738     assertThat(
739             icingSearchEngine
740                 .setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false)
741                 .getStatus()
742                 .getCode())
743         .isEqualTo(StatusProto.Code.OK);
744 
745     DocumentProto emailDocument1 =
746         createEmailDocument("namespace", "uri1").toBuilder()
747             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("fo"))
748             .build();
749     DocumentProto emailDocument2 =
750         createEmailDocument("namespace", "uri2").toBuilder()
751             .addProperties(PropertyProto.newBuilder().setName("subject").addStringValues("foo"))
752             .build();
753     assertStatusOk(icingSearchEngine.put(emailDocument1).getStatus());
754     assertStatusOk(icingSearchEngine.put(emailDocument2).getStatus());
755 
756     SuggestionSpecProto suggestionSpec =
757         SuggestionSpecProto.newBuilder()
758             .setPrefix("f")
759             .setNumToReturn(10)
760             .setScoringSpec(
761                 SuggestionScoringSpecProto.newBuilder()
762                     .setScoringMatchType(Code.EXACT_ONLY)
763                     .build())
764             .build();
765 
766     SuggestionResponse response = icingSearchEngine.searchSuggestions(suggestionSpec);
767     assertStatusOk(response.getStatus());
768     assertThat(response.getSuggestionsList()).hasSize(2);
769     assertThat(response.getSuggestions(0).getQuery()).isEqualTo("foo");
770     assertThat(response.getSuggestions(1).getQuery()).isEqualTo("fo");
771   }
772 
773   @Test
testLogging()774   public void testLogging() throws Exception {
775     // Set to INFO
776     assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.INFO)).isTrue();
777     assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.INFO)).isTrue();
778     assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.DBG)).isFalse();
779 
780     // Set to WARNING
781     assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.WARNING)).isTrue();
782     assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.WARNING)).isTrue();
783     assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.INFO)).isFalse();
784 
785     // Set to DEBUG
786     assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.DBG)).isTrue();
787     assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.DBG)).isTrue();
788     assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE)).isFalse();
789 
790     // Set to VERBOSE
791     assertThat(IcingSearchEngine.setLoggingLevel(LogSeverity.Code.VERBOSE, (short) 1)).isTrue();
792     assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE, (short) 1)).isTrue();
793     assertThat(IcingSearchEngine.shouldLog(LogSeverity.Code.VERBOSE, (short) 2)).isFalse();
794 
795     assertThat(IcingSearchEngine.getLoggingTag()).isNotEmpty();
796   }
797 
assertStatusOk(StatusProto status)798   private static void assertStatusOk(StatusProto status) {
799     assertWithMessage(status.getMessage()).that(status.getCode()).isEqualTo(StatusProto.Code.OK);
800   }
801 }
802