// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespaxmlparser;
import com.yahoo.document.*;
import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.IntegerFieldValue;
import com.yahoo.document.datatypes.PredicateFieldValue;
import com.yahoo.document.datatypes.Struct;
import com.yahoo.document.predicate.BinaryFormat;
import com.yahoo.document.predicate.Conjunction;
import com.yahoo.document.predicate.FeatureRange;
import com.yahoo.document.predicate.FeatureSet;
import com.yahoo.document.predicate.Predicate;
import com.yahoo.document.serialization.DeserializationException;
import com.yahoo.tensor.TensorType;
import org.apache.commons.codec.binary.Base64;
import org.junit.Test;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
/**
* @author Simon Thoresen Hult
*/
public class VespaXmlFieldReaderTestCase {
@Test
public void requireThatPredicateFieldValuesCanBeRead() throws Exception {
assertReadable(new Conjunction(new FeatureSet("foo", "bar"),
new FeatureRange("baz", 6L, 9L)));
}
@Test
public void requireThatArrayItemDeserializeExceptionIncludesFieldName() throws Exception {
assertThrows(new Field("my_field", DataType.getArray(DataType.BYTE)),
"- -129
",
"Field 'my_field': Invalid byte \"-129\". (at line 1, column 70)");
}
@Test
public void requireThatMapKeyDeserializeExceptionIncludesFieldName() throws Exception {
assertThrows(new Field("my_field", DataType.getMap(DataType.BYTE, DataType.STRING)),
"- -129foo
",
"Field 'my_field': Invalid byte \"-129\". (at line 1, column 74)");
}
@Test
public void requireThatMapValueDeserializeExceptionIncludesFieldName() throws Exception {
assertThrows(new Field("my_field", DataType.getMap(DataType.STRING, DataType.BYTE)),
"- foo-129
",
"Field 'my_field': Invalid byte \"-129\". (at line 1, column 92)");
}
@Test
public void requireThatStructFieldDeserializeExceptionIncludesFieldName() throws Exception {
StructDataType structType = new StructDataType("my_struct");
structType.addField(new Field("my_byte", DataType.BYTE));
assertThrows(new Field("my_field", structType),
"-129",
"Field 'my_byte': Invalid byte \"-129\". (at line 1, column 76)");
}
@Test
public void requireThatWSetItemDeserializeExceptionIncludesFieldName() throws Exception {
assertThrows(new Field("my_field", DataType.getWeightedSet(DataType.BYTE)),
"- -129
",
"Field 'my_field': Invalid byte \"-129\". (at line 1, column 70)");
}
@Test
public void requireThatPutsForTensorFieldsAreNotSupported() throws Exception {
assertThrows(new Field("my_tensor", new TensorDataType(TensorType.empty)), "",
"Field 'my_tensor': XML input for fields of type TENSOR is not supported. Please use JSON input instead.");
}
private class MockedReaderFixture {
public DocumentTypeManager mgr;
public DocumentType docType;
public XMLStreamReader xmlReader;
public VespaXMLFieldReader fieldReader;
public MockedReaderFixture() {
mgr = new DocumentTypeManager();
mgr.register(PositionDataType.INSTANCE);
docType = new DocumentType("my_doc");
docType.addField("my_pos", PositionDataType.INSTANCE);
mgr.registerDocumentType(docType);
xmlReader = mock(XMLStreamReader.class);
fieldReader = new VespaXMLFieldReader(xmlReader, mgr);
}
public void assertReadPositionEquals(int x, int y) {
Struct pos = new Struct(PositionDataType.INSTANCE);
fieldReader.read(docType.getField("my_pos"), pos);
assertEquals(new IntegerFieldValue(x), pos.getFieldValue(PositionDataType.FIELD_X));
assertEquals(new IntegerFieldValue(y), pos.getFieldValue(PositionDataType.FIELD_Y));
}
}
@Test
public void requireThatPositionFieldCanBeReadInSingleEvent() throws Exception {
MockedReaderFixture fixture = new MockedReaderFixture();
XMLStreamReader xmlReader = fixture.xmlReader;
when(xmlReader.getAttributeCount()).thenReturn(0);
when(xmlReader.hasNext()).thenReturn(true, true, false);
when(xmlReader.next()).thenReturn(
XMLStreamReader.CHARACTERS, XMLStreamReader.END_ELEMENT);
when(xmlReader.getText()).thenReturn("E3;N4");
fixture.assertReadPositionEquals(3000000, 4000000);
}
@Test
public void requireThatPositionFieldCanBeReadAcrossMultipleEvents() throws Exception {
MockedReaderFixture fixture = new MockedReaderFixture();
XMLStreamReader xmlReader = fixture.xmlReader;
when(xmlReader.getAttributeCount()).thenReturn(0);
when(xmlReader.hasNext()).thenReturn(true, true, true, true, false);
when(xmlReader.next()).thenReturn(
XMLStreamReader.CHARACTERS, XMLStreamReader.CHARACTERS,
XMLStreamReader.CHARACTERS, XMLStreamReader.END_ELEMENT);
when(xmlReader.getText()).thenReturn("E3;", "N", "4");
fixture.assertReadPositionEquals(3000000, 4000000);
}
private static void assertThrows(Field field, String fieldXml, String expected) throws Exception {
DocumentTypeManager docManager = new DocumentTypeManager();
DocumentType docType = new DocumentType("my_type");
docType.addField(field);
docManager.register(docType);
String documentXml = "<" + field.getName() + ">" +
fieldXml + "" + field.getName() + ">";
InputStream in = new ByteArrayInputStream(documentXml.getBytes(StandardCharsets.UTF_8));
Document doc = new Document(docType, "doc:scheme:");
try {
new VespaXMLFieldReader(in, docManager).read(null, doc);
fail();
} catch (DeserializationException e) {
assertEquals(expected, e.getMessage());
}
}
private static void assertReadable(Predicate predicate) throws Exception {
assertRead(predicate,
"" +
" " + predicate + "" +
"");
assertRead(predicate,
"" +
" " +
Base64.encodeBase64String(BinaryFormat.encode(predicate)) +
" " +
"");
}
private static void assertRead(Predicate expected, String documentXml) throws Exception {
DocumentTypeManager docManager = new DocumentTypeManager();
DocumentType docType = new DocumentType("my_type");
docType.addField("my_predicate", DataType.PREDICATE);
docManager.register(docType);
InputStream in = new ByteArrayInputStream(documentXml.getBytes(StandardCharsets.UTF_8));
Document doc = new Document(docType, "doc:scheme:");
new VespaXMLFieldReader(in, docManager).read(null, doc);
FieldValue value = doc.getFieldValue("my_predicate");
assertTrue(value instanceof PredicateFieldValue);
assertEquals(expected, ((PredicateFieldValue)value).getPredicate());
}
}