/*
* Copyright (C) 2012-2015 DataStax Inc.
*
* 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.datastax.driver.core;
import com.datastax.driver.core.UserType.Field;
import com.datastax.driver.core.exceptions.CodecNotFoundException;
import com.datastax.driver.core.exceptions.InvalidTypeException;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import org.testng.annotations.Test;
import java.nio.ByteBuffer;
import java.util.*;
import static com.datastax.driver.core.Assertions.assertThat;
import static com.datastax.driver.core.DataType.*;
import static com.datastax.driver.core.ProtocolVersion.V3;
import static com.google.common.collect.Lists.newArrayList;
import static org.testng.Assert.fail;
public class TypeCodecTest {
public static final DataType CUSTOM_FOO = DataType.custom("com.example.FooBar");
// @formatter:off
public static final TypeToken<List<A>> LIST_OF_A_TOKEN = new TypeToken<List<A>>() {};
public static final TypeToken<List<B>> LIST_OF_B_TOKEN = new TypeToken<List<B>>() {};
// @formatter:on
private CodecRegistry codecRegistry = new CodecRegistry();
@Test(groups = "unit")
public void testCustomList() throws Exception {
DataType cqlType = list(CUSTOM_FOO);
TypeCodec<List<?>> codec = codecRegistry.codecFor(cqlType);
assertThat(codec).isNotNull().accepts(cqlType);
}
@Test(groups = "unit")
public void testCustomSet() throws Exception {
DataType cqlType = set(CUSTOM_FOO);
TypeCodec<Set<?>> codec = codecRegistry.codecFor(cqlType);
assertThat(codec).isNotNull().accepts(cqlType);
}
@Test(groups = "unit")
public void testCustomKeyMap() throws Exception {
DataType cqlType = map(CUSTOM_FOO, text());
TypeCodec<Map<?, ?>> codec = codecRegistry.codecFor(cqlType);
assertThat(codec).isNotNull().accepts(cqlType);
}
@Test(groups = "unit")
public void testCustomValueMap() throws Exception {
DataType cqlType = map(text(), CUSTOM_FOO);
TypeCodec<Map<?, ?>> codec = codecRegistry.codecFor(cqlType);
assertThat(codec).isNotNull().accepts(cqlType);
}
@Test(groups = "unit", expectedExceptions = {IllegalArgumentException.class})
public void collectionTooLargeTest() throws Exception {
DataType cqlType = DataType.list(DataType.cint());
List<Integer> list = Collections.nCopies(65536, 1);
TypeCodec<List<?>> codec = codecRegistry.codecFor(cqlType);
codec.serialize(list, ProtocolVersion.V2);
}
@Test(groups = "unit", expectedExceptions = {IllegalArgumentException.class})
public void collectionElementTooLargeTest() throws Exception {
DataType cqlType = DataType.list(DataType.text());
List<String> list = newArrayList(Strings.repeat("a", 65536));
TypeCodec<List<?>> codec = codecRegistry.codecFor(cqlType);
codec.serialize(list, ProtocolVersion.V2);
}
@Test(groups = "unit")
public void test_cql_list_varchar_to_list_list_integer() {
ListVarcharToListListInteger codec = new ListVarcharToListListInteger();
List<List<Integer>> list = new ArrayList<List<Integer>>();
list.add(newArrayList(1, 2, 3));
list.add(newArrayList(4, 5, 6));
assertThat(codec).canSerialize(list);
}
@Test(groups = "unit")
public void test_ascii_vs_utf8() {
TypeCodec<String> asciiCodec = TypeCodec.ascii();
TypeCodec<String> utf8Codec = TypeCodec.varchar();
String ascii = "The quick brown fox jumps over the lazy dog!";
String utf8 = "Dès Noël, où un zéphyr haï me vêt de glaçons würmiens, je dîne d’exquis rôtis de bœuf au kir à l’aÿ d’âge mûr & cætera!";
assertThat(asciiCodec)
.accepts(String.class)
.accepts(ascii())
.doesNotAccept(varchar())
.doesNotAccept(text())
.accepts(ascii)
.canSerialize(ascii)
.cannotSerialize(utf8);
assertThat(utf8Codec)
.accepts(String.class)
.doesNotAccept(ascii())
.accepts(varchar())
.accepts(text())
.accepts(ascii)
.accepts(utf8)
.canSerialize(ascii)
.canSerialize(utf8);
}
@Test(groups = "unit")
public void test_varchar_vs_text() {
assertThat(TypeCodec.varchar())
.accepts(String.class)
.accepts(varchar())
.accepts(text());
assertThat(TypeCodec.list(TypeCodec.varchar()))
.accepts(list(varchar()))
.accepts(list(text()));
assertThat(TypeCodec.set(TypeCodec.varchar()))
.accepts(set(varchar()))
.accepts(set(text()));
assertThat(TypeCodec.map(TypeCodec.varchar(), TypeCodec.varchar()))
.accepts(map(varchar(), varchar()))
.accepts(map(varchar(), text()))
.accepts(map(text(), varchar()))
.accepts(map(text(), text()));
TupleType t1 = new TupleType(newArrayList(varchar(), varchar()), V3, new CodecRegistry());
TupleType t2 = new TupleType(newArrayList(text(), varchar()), V3, new CodecRegistry());
TupleType t3 = new TupleType(newArrayList(varchar(), text()), V3, new CodecRegistry());
TupleType t4 = new TupleType(newArrayList(text(), text()), V3, new CodecRegistry());
assertThat(TypeCodec.tuple(t1))
.accepts(t2)
.accepts(t3)
.accepts(t4);
UserType u1 = new UserType("ks", "table", false, newArrayList(new Field("f1", varchar()), new Field("f2", varchar())), V3, new CodecRegistry());
UserType u2 = new UserType("ks", "table", false, newArrayList(new Field("f1", text()), new Field("f2", varchar())), V3, new CodecRegistry());
UserType u3 = new UserType("ks", "table", false, newArrayList(new Field("f1", varchar()), new Field("f2", text())), V3, new CodecRegistry());
UserType u4 = new UserType("ks", "table", false, newArrayList(new Field("f1", text()), new Field("f2", text())), V3, new CodecRegistry());
assertThat(TypeCodec.userType(u1))
.accepts(u2)
.accepts(u3)
.accepts(u4);
}
@Test(groups = "unit")
public void test_inheritance() {
CodecRegistry codecRegistry = new CodecRegistry();
ACodec aCodec = new ACodec();
codecRegistry.register(aCodec);
assertThat(codecRegistry.codecFor(cint(), A.class)).isNotNull().isSameAs(aCodec);
try {
// covariance not accepted: no codec handles B exactly
codecRegistry.codecFor(cint(), B.class);
fail();
} catch (CodecNotFoundException e) {
//ok
}
TypeCodec<List<A>> expected = TypeCodec.list(aCodec);
TypeCodec<List<A>> actual = codecRegistry.codecFor(list(cint()), LIST_OF_A_TOKEN);
assertThat(actual.getCqlType()).isEqualTo(expected.getCqlType());
assertThat(actual.getJavaType()).isEqualTo(expected.getJavaType());
// cannot work: List<B> is not assignable to List<A>
try {
codecRegistry.codecFor(list(cint()), LIST_OF_B_TOKEN);
fail();
} catch (CodecNotFoundException e) {
//ok
}
codecRegistry = new CodecRegistry();
BCodec bCodec = new BCodec();
codecRegistry.register(bCodec);
try {
assertThat(codecRegistry.codecFor(cint(), A.class));
fail();
} catch (CodecNotFoundException e) {
// ok
}
assertThat(codecRegistry.codecFor(cint(), B.class)).isNotNull().isSameAs(bCodec);
try {
codecRegistry.codecFor(list(cint()), LIST_OF_A_TOKEN);
fail();
} catch (CodecNotFoundException e) {
// ok
}
TypeCodec<List<B>> expectedB = TypeCodec.list(bCodec);
TypeCodec<List<B>> actualB = codecRegistry.codecFor(list(cint()), LIST_OF_B_TOKEN);
assertThat(actualB.getCqlType()).isEqualTo(expectedB.getCqlType());
assertThat(actualB.getJavaType()).isEqualTo(expectedB.getJavaType());
}
@Test(groups = "unit")
public void should_deserialize_empty_buffer_as_tuple_with_null_values() {
CodecRegistry codecRegistry = new CodecRegistry();
TupleType tupleType = new TupleType(newArrayList(DataType.cint(), DataType.varchar(), DataType.cfloat()), ProtocolVersion.NEWEST_SUPPORTED, codecRegistry);
TupleValue expected = tupleType.newValue(null, null, null);
TupleValue actual = codecRegistry.codecFor(tupleType, TupleValue.class).deserialize(ByteBuffer.allocate(0), ProtocolVersion.NEWEST_SUPPORTED);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
}
@Test(groups = "unit")
public void should_deserialize_empty_buffer_as_udt_with_null_values() {
CodecRegistry codecRegistry = new CodecRegistry();
UserType udt = new UserType("ks", "t", false, Arrays.asList(
new UserType.Field("t", DataType.text()),
new UserType.Field("i", DataType.cint()),
new UserType.Field("l", DataType.list(DataType.text()))
), ProtocolVersion.NEWEST_SUPPORTED, codecRegistry);
UDTValue expected = udt.newValue();
expected.setString("t", null);
expected.setToNull("i");
expected.setList("l", null);
UDTValue actual = codecRegistry.codecFor(udt, UDTValue.class).deserialize(ByteBuffer.allocate(0), ProtocolVersion.NEWEST_SUPPORTED);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
}
/**
* Ensures that {@link TypeCodec#timeUUID()} is resolved for all UUIDs and throws an
* {@link InvalidTypeException} when attempting to serialize or format a non-type 1
* UUID.
*
* @jira_ticket JAVA-965
*/
@Test(groups = "unit")
public void should_resolve_timeuuid_codec_for_all_uuids_and_fail_to_serialize_non_type1_uuid() {
UUID type4UUID = UUID.randomUUID();
TypeCodec<UUID> codec = codecRegistry.codecFor(DataType.timeuuid(), type4UUID);
// Should resolve the TimeUUIDCodec, but not serialize/format a type4 uuid with it.
assertThat(codec).isSameAs(TypeCodec.timeUUID())
.accepts(UUID.class)
.cannotSerialize(type4UUID)
.cannotFormat(type4UUID);
}
/**
* Ensures that primitive types are correctly handled and wrapped when necessary.
*/
@Test(groups = "unit")
public void should_wrap_primitive_types() {
assertThat(TypeCodec.cboolean())
.accepts(Boolean.class)
.accepts(Boolean.TYPE)
.accepts(true);
assertThat(TypeCodec.cint())
.accepts(Integer.class)
.accepts(Integer.TYPE)
.accepts(42);
assertThat(TypeCodec.bigint())
.accepts(Long.class)
.accepts(Long.TYPE)
.accepts(42L);
assertThat(TypeCodec.cfloat())
.accepts(Float.class)
.accepts(Float.TYPE)
.accepts(42.0F);
assertThat(TypeCodec.cdouble())
.accepts(Double.class)
.accepts(Double.TYPE)
.accepts(42.0D);
}
private class ListVarcharToListListInteger extends TypeCodec<List<List<Integer>>> {
private final TypeCodec<List<String>> codec = TypeCodec.list(TypeCodec.varchar());
protected ListVarcharToListListInteger() {
super(DataType.list(DataType.varchar()), TypeTokens.listOf(TypeTokens.listOf(Integer.class)));
}
@Override
public ByteBuffer serialize(List<List<Integer>> value, ProtocolVersion protocolVersion) {
return codec.serialize(Lists.transform(value, new Function<List<Integer>, String>() {
@Override
public String apply(List<Integer> input) {
return Joiner.on(",").join(input);
}
}), protocolVersion);
}
@Override
public List<List<Integer>> deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) {
return Lists.transform(codec.deserialize(bytes, protocolVersion), new Function<String, List<Integer>>() {
@Override
public List<Integer> apply(String input) {
return Lists.transform(Arrays.asList(input.split(",")), new Function<String, Integer>() {
@Override
public Integer apply(String input) {
return Integer.parseInt(input);
}
});
}
});
}
@Override
public List<List<Integer>> parse(String value) {
throw new UnsupportedOperationException();
}
@Override
public String format(List<List<Integer>> value) {
throw new UnsupportedOperationException();
}
}
class A {
int i = 0;
}
class B extends A {
{
i = 1;
}
}
class ACodec extends TypeCodec<A> {
protected ACodec() {
super(DataType.cint(), A.class);
}
@Override
public ByteBuffer serialize(A value, ProtocolVersion protocolVersion) throws InvalidTypeException {
return null; // not tested
}
@Override
public A deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) throws InvalidTypeException {
return null; // not tested
}
@Override
public A parse(String value) throws InvalidTypeException {
return null; // not tested
}
@Override
public String format(A value) throws InvalidTypeException {
return null; // not tested
}
}
class BCodec extends TypeCodec<B> {
protected BCodec() {
super(DataType.cint(), B.class);
}
@Override
public ByteBuffer serialize(B value, ProtocolVersion protocolVersion) throws InvalidTypeException {
return null; // not tested
}
@Override
public B deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) throws InvalidTypeException {
return null; // not tested
}
@Override
public B parse(String value) throws InvalidTypeException {
return null; // not tested
}
@Override
public String format(B value) throws InvalidTypeException {
return null; // not tested
}
}
}