blob: e0ab019bbdc300809804cb02d3f763dee4c96142 [file] [log] [blame]
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.index;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.google.gerrit.entities.converter.ChangeProtoConverter;
import com.google.gerrit.index.IndexedField;
import com.google.gerrit.index.SchemaFieldDefs.Getter;
import com.google.gerrit.index.SchemaFieldDefs.Setter;
import com.google.gerrit.index.StoredValue;
import com.google.gerrit.index.testing.FakeStoredValue;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.Entities.Change;
import com.google.gerrit.proto.Entities.Change_Id;
import com.google.gerrit.proto.Protos;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.util.Map.Entry;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.FromDataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
/** Tests for {@link com.google.gerrit.index.IndexedField} */
@SuppressWarnings("serial")
@RunWith(Theories.class)
public class IndexedFieldTest {
/** Test input object for {@link IndexedField} */
static class TestIndexedData {
private Object testField;
public Object getTestField() {
return testField;
}
public void setTestField(Object testField) {
this.testField = testField;
}
}
private static class TestIndexedDataSetter<T> implements Setter<TestIndexedData, T> {
@Override
public void set(TestIndexedData testIndexedData, T value) {
testIndexedData.setTestField(value);
}
}
@SuppressWarnings("unchecked")
private static class TestIndexedDataGetter<T> implements Getter<TestIndexedData, T> {
@Override
public T get(TestIndexedData input) throws IOException {
return (T) input.getTestField();
}
}
public static <T> TestIndexedDataSetter<T> setter() {
return new TestIndexedDataSetter<>();
}
public static <T> TestIndexedDataGetter<T> getter() {
return new TestIndexedDataGetter<>();
}
static final IndexedField<TestIndexedData, Integer> INTEGER_FIELD =
IndexedField.<TestIndexedData>integerBuilder("TestField").build(getter(), setter());
static final IndexedField<TestIndexedData, Integer>.SearchSpec INTEGER_FIELD_SPEC =
INTEGER_FIELD.integer("test");
static final IndexedField<TestIndexedData, Iterable<Integer>> ITERABLE_INTEGER_FIELD =
IndexedField.<TestIndexedData>iterableIntegerBuilder("TestField").build(getter(), setter());
static final IndexedField<TestIndexedData, Iterable<Integer>>.SearchSpec
ITERABLE_INTEGER_FIELD_SPEC = ITERABLE_INTEGER_FIELD.integer("test");
static final IndexedField<TestIndexedData, Iterable<String>> ITERABLE_STRING_FIELD =
IndexedField.<TestIndexedData>iterableStringBuilder("TestField").build(getter(), setter());
static final IndexedField<TestIndexedData, Long> LONG_FIELD =
IndexedField.<TestIndexedData>longBuilder("TestField").build(getter(), setter());
static final IndexedField<TestIndexedData, Long>.SearchSpec LONG_FIELD_SPEC =
LONG_FIELD.longSearch("test");
static final IndexedField<TestIndexedData, Timestamp> TIMESTAMP_FIELD =
IndexedField.<TestIndexedData>timestampBuilder("TestField").build(getter(), setter());
static final IndexedField<TestIndexedData, Timestamp>.SearchSpec TIMESTAMP_FIELD_SPEC =
TIMESTAMP_FIELD.timestamp("test");
static final IndexedField<TestIndexedData, Iterable<String>>.SearchSpec
ITERABLE_STRING_FIELD_SPEC = ITERABLE_STRING_FIELD.fullText("test");
static final IndexedField<TestIndexedData, String> STRING_FIELD =
IndexedField.<TestIndexedData>stringBuilder("TestField").build(getter(), setter());
static final IndexedField<TestIndexedData, String>.SearchSpec STRING_FIELD_SPEC =
STRING_FIELD.fullText("test");
static final IndexedField<TestIndexedData, Iterable<byte[]>> ITERABLE_STORED_BYTE_FIELD =
IndexedField.<TestIndexedData>iterableByteArrayBuilder("TestField")
.stored()
.build(getter(), setter());
static final IndexedField<TestIndexedData, Iterable<byte[]>>.SearchSpec
ITERABLE_STORED_BYTE_SPEC = ITERABLE_STORED_BYTE_FIELD.storedOnly("test");
static final IndexedField<TestIndexedData, byte[]> STORED_BYTE_FIELD =
IndexedField.<TestIndexedData>byteArrayBuilder("TestField")
.stored()
.build(getter(), setter());
static final IndexedField<TestIndexedData, byte[]>.SearchSpec STORED_BYTE_SPEC =
STORED_BYTE_FIELD.storedOnly("test");
static final IndexedField<TestIndexedData, Entities.Change> STORED_PROTO_FIELD =
IndexedField.<TestIndexedData, Entities.Change>builder(
"TestChange", new TypeToken<Entities.Change>() {})
.stored()
.build(getter(), setter(), ChangeProtoConverter.INSTANCE);
static final IndexedField<TestIndexedData, Entities.Change>.SearchSpec STORED_PROTO_FIELD_SPEC =
STORED_PROTO_FIELD.storedOnly("test_change");
static final IndexedField<TestIndexedData, Iterable<Entities.Change>>
ITERABLE_STORED_PROTO_FIELD =
IndexedField.<TestIndexedData, Iterable<Entities.Change>>builder(
"TestChange", new TypeToken<Iterable<Entities.Change>>() {})
.stored()
.build(getter(), setter(), ChangeProtoConverter.INSTANCE);
static final IndexedField<TestIndexedData, Iterable<Entities.Change>>.SearchSpec
ITERABLE_PROTO_FIELD_SPEC = ITERABLE_STORED_PROTO_FIELD.storedOnly("test_change");
@DataPoints("nonProtoTypes")
public static final ImmutableList<
Entry<IndexedField<TestIndexedData, ?>.SearchSpec, Serializable>>
fieldToStoredValue =
new ImmutableMap.Builder<IndexedField<TestIndexedData, ?>.SearchSpec, Serializable>()
.put(INTEGER_FIELD_SPEC, 123456)
.put(ITERABLE_INTEGER_FIELD_SPEC, ImmutableList.of(123456, 654321))
.put(LONG_FIELD_SPEC, 123456L)
.put(TIMESTAMP_FIELD_SPEC, new Timestamp(1234567L))
.put(STRING_FIELD_SPEC, "123456")
.put(ITERABLE_STRING_FIELD_SPEC, ImmutableList.of("123456"))
.put(
ITERABLE_STORED_BYTE_SPEC,
ImmutableList.of("123456".getBytes(StandardCharsets.UTF_8)))
.put(STORED_BYTE_SPEC, "123456".getBytes(StandardCharsets.UTF_8))
.build()
.entrySet()
.asList();
@DataPoints("protoTypes")
public static final ImmutableList<
Entry<IndexedField<TestIndexedData, ?>.SearchSpec, Serializable>>
protoFieldToStoredValue =
ImmutableMap.<IndexedField<TestIndexedData, ?>.SearchSpec, Serializable>of(
STORED_PROTO_FIELD_SPEC,
createChangeProto(12345),
ITERABLE_PROTO_FIELD_SPEC,
ImmutableList.of(createChangeProto(12345), createChangeProto(54321)))
.entrySet()
.asList();
@Theory
public void testSetIfPossible(
@FromDataPoints("nonProtoTypes")
Entry<IndexedField<TestIndexedData, StoredValue>.SearchSpec, StoredValue>
fieldToStoredValue) {
Object docValue = fieldToStoredValue.getValue();
IndexedField<TestIndexedData, StoredValue>.SearchSpec searchSpec = fieldToStoredValue.getKey();
StoredValue storedValue = new FakeStoredValue(fieldToStoredValue.getValue());
TestIndexedData testIndexedData = new TestIndexedData();
searchSpec.setIfPossible(testIndexedData, storedValue);
assertThat(testIndexedData.getTestField()).isEqualTo(docValue);
}
@Test
public void testSetIfPossible_protoFromBytes() {
Entities.Change changeProto = createChangeProto(12345);
StoredValue storedValue = new FakeStoredValue(Protos.toByteArray(changeProto));
TestIndexedData testIndexedData = new TestIndexedData();
STORED_PROTO_FIELD_SPEC.setIfPossible(testIndexedData, storedValue);
assertThat(testIndexedData.getTestField()).isEqualTo(changeProto);
}
@Test
public void testSetIfPossible_iterableProtoFromIterableBytes() {
ImmutableList<Entities.Change> changeProtos =
ImmutableList.of(createChangeProto(12345), createChangeProto(54321));
StoredValue storedValue =
new FakeStoredValue(
changeProtos.stream()
.map(proto -> Protos.toByteArray(proto))
.collect(toImmutableList()));
TestIndexedData testIndexedData = new TestIndexedData();
ITERABLE_STORED_PROTO_FIELD.setIfPossible(testIndexedData, storedValue);
assertThat(testIndexedData.getTestField()).isEqualTo(changeProtos);
}
@Theory
public void testSetIfPossible_fromProto(
@FromDataPoints("protoTypes")
Entry<IndexedField<TestIndexedData, StoredValue>.SearchSpec, StoredValue>
fieldToStoredValue) {
Object docValue = fieldToStoredValue.getValue();
IndexedField<TestIndexedData, StoredValue>.SearchSpec searchSpec = fieldToStoredValue.getKey();
StoredValue storedValue = new FakeStoredValue(fieldToStoredValue.getValue(), /*isProto=*/ true);
TestIndexedData testIndexedData = new TestIndexedData();
searchSpec.setIfPossible(testIndexedData, storedValue);
assertThat(testIndexedData.getTestField()).isEqualTo(docValue);
}
@Test
public void test_isProtoType() {
assertThat(STORED_PROTO_FIELD.isProtoType()).isTrue();
assertThat(ITERABLE_STORED_PROTO_FIELD.isProtoType()).isFalse();
assertThat(INTEGER_FIELD.isProtoType()).isFalse();
assertThat(ITERABLE_STRING_FIELD.isProtoType()).isFalse();
assertThat(STORED_BYTE_FIELD.isProtoType()).isFalse();
assertThat(ITERABLE_STORED_BYTE_FIELD.isProtoType()).isFalse();
}
@Test
public void test_isProtoIterableType() {
assertThat(ITERABLE_STORED_PROTO_FIELD.isProtoIterableType()).isTrue();
assertThat(STORED_PROTO_FIELD.isProtoIterableType()).isFalse();
assertThat(INTEGER_FIELD.isProtoIterableType()).isFalse();
assertThat(ITERABLE_STRING_FIELD.isProtoIterableType()).isFalse();
assertThat(STORED_BYTE_FIELD.isProtoIterableType()).isFalse();
assertThat(ITERABLE_STORED_BYTE_FIELD.isProtoType()).isFalse();
}
private static Change createChangeProto(int id) {
return Entities.Change.newBuilder()
.setChangeId(Change_Id.newBuilder().setId(id).build())
.build();
}
}