/**
* Global Sensor Networks (GSN) Source Code
* Copyright (c) 2006-2016, Ecole Polytechnique Federale de Lausanne (EPFL)
*
* This file is part of GSN.
*
* GSN is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GSN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GSN. If not, see <http://www.gnu.org/licenses/>.
*
* File: src/ch/epfl/gsn/storage/hibernate/TestHibernateStorage.java
*
* @author Timotee Maret
*
*/
package ch.epfl.gsn.storage.hibernate;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ch.epfl.gsn.beans.DataField;
import ch.epfl.gsn.beans.DataTypes;
import ch.epfl.gsn.beans.StreamElement;
import ch.epfl.gsn.storage.DataEnumeratorIF;
import ch.epfl.gsn.storage.hibernate.DBConnectionInfo;
import ch.epfl.gsn.storage.hibernate.HibernateStorage;
import java.io.Serializable;
import java.util.*;
public class TestHibernateStorage {
private ArrayList<DataField> dataField = null;
private static DBConnectionInfo dbInfo = null;
@BeforeClass
public static void initClass() {
//dbInfo = new HibernateUtil.DBConnectionInfo("org.h2.Driver", "jdbc:h2:mem:test", "sa", "");
dbInfo = new DBConnectionInfo("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/ch.epfl.gsn", "root", "");
}
@Before
public void setup() {
// Contains all the type of fields defined in the class: DataTypes
dataField = new ArrayList<DataField>();
dataField.add(new DataField("f_varchar", "varchar(256)"));
dataField.add(new DataField("f_char", "char(256)"));
dataField.add(new DataField("f_integer", "integer"));
dataField.add(new DataField("f_bigint", "bigint"));
dataField.add(new DataField("f_binary", "binary"));
dataField.add(new DataField("f_double", "double"));
dataField.add(new DataField("f_time", "time"));
dataField.add(new DataField("f_tinyint", "tinyint"));
dataField.add(new DataField("f_smallint", "smallint"));
}
@Test
public void testSuccessfulTableCreation() {
String hm;
// 1. Successful Creation with unique timed set
HibernateStorage storage1 = HibernateStorage.newInstance(dbInfo, "testSuccessfulTableCreation1", dataField.toArray(new DataField[]{}), true);
assertNotNull(storage1);
// 2. Successful creation with unique timed unset
HibernateStorage storage2 = HibernateStorage.newInstance(dbInfo, "testSuccessfulTableCreation2", dataField.toArray(new DataField[]{}), false);
assertNotNull(storage2);
}
@Test
public void testFailedTableCreation() {
ArrayList<DataField> dfs;
// 1. non valid field name
dfs = new ArrayList<DataField>(dataField);
dfs.add(new DataField("f_integer f", "integer")); // We add a non valid field name
HibernateStorage storage1 = HibernateStorage.newInstance(dbInfo, "testFailedTableCreation1", dfs.toArray(new DataField[]{}), true);
assertNull(storage1);
// 2. non valid field name
dfs = new ArrayList<DataField>(dataField);
dfs.add(new DataField("f_integer-f", "integer")); // We add a non valid field name
HibernateStorage storage2 = HibernateStorage.newInstance(dbInfo, "testFailedTableCreation2", dfs.toArray(new DataField[]{}), true);
assertNull(storage1);
// 3. non valid identifier name
dfs = new ArrayList<DataField>(dataField);
HibernateStorage storage3 = HibernateStorage.newInstance(dbInfo, "testFailedTableCre ation3", dfs.toArray(new DataField[]{}), true); // Invalid identifier name
assertNull(storage1);
// 4. duplicated identifier name
dfs = new ArrayList<DataField>(dataField);
//Duplicate the first field
dfs.add(new DataField(dfs.get(0).getName(), dfs.get(0).getDataTypeID()));
HibernateStorage storage4 = HibernateStorage.newInstance(dbInfo, "testFailedTableCreation4", dfs.toArray(new DataField[]{}), true); // Invalid identifier name
assertNull(storage1);
}
@Test
public void testMinMaxStorageOfData() {
DataField[] structure = dataField.toArray(new DataField[]{});
//
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, "testMinMaxStorageOfData", dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
// Build the StreamElement containing the min values for each fields.
StreamElement seMin = generateStreamElement(structure, Byte.MIN_VALUE);
seMin.setTimeStamp(100);
// Build the StreamElement containinf the max values for each fields.
StreamElement seMax = generateStreamElement(structure, Byte.MAX_VALUE);
seMax.setTimeStamp(200);
// Store them
Serializable pkMin = storage.saveStreamElement(seMin);
assertNotNull(pkMin);
Serializable pkMax = storage.saveStreamElement(seMax);
assertNotNull(pkMax);
// Retrieve them
StreamElement seMinOut = storage.getStreamElement(pkMin);
assertNotNull(seMinOut);
StreamElement seMaxOut = storage.getStreamElement(pkMax);
assertNotNull(seMaxOut);
//
assertEquals(seMin.getTimeStamp(), seMinOut.getTimeStamp());
assertEquals(seMax.getTimeStamp(), seMaxOut.getTimeStamp());
for (DataField df : structure) {
if (df.getDataTypeID() != DataTypes.BINARY) {
assertEquals(seMin.getData(df.getName()), seMinOut.getData(df.getName()));
assertEquals(seMax.getData(df.getName()), seMaxOut.getData(df.getName()));
}
}
}
@Test
public void testCorrectInsertWithExtraField() {
DataField[] structure = dataField.toArray(new DataField[]{});
//
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, "testCorrectInsertWithExtraField", dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
// Build a StreamElement with an extra field compared to the structure.
dataField.add(new DataField("extraField", DataTypes.INTEGER));
StreamElement se = generateStreamElement(dataField.toArray(new DataField[]{}), Byte.MIN_VALUE);
se.setTimeStamp(100);
//
Serializable pk = storage.saveStreamElement(se);
assertNotNull(pk);
StreamElement seOut = storage.getStreamElement(pk);
// Check that no field is null in the fetched StreamElement
for (DataField df : structure) {
assertNotNull(seOut.getData(df.getName()));
}
// Check that the values are equals
for (int i = 0; i < seOut.getData().length; i++) {
if (seOut.getFieldTypes()[i] != DataTypes.BINARY)
assertEquals(se.getData(seOut.getFieldNames()[i]), seOut.getData()[i]);
}
}
@Test
public void testCorrectInsertWithLessField() {
DataField[] structure = dataField.toArray(new DataField[]{});
//
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, "testCorrectInsertWithLessField", dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
// Build a StreamElement which miss a field compared to the structure.
DataField removed = dataField.remove(0);
StreamElement se = generateStreamElement(dataField.toArray(new DataField[]{}), Byte.MIN_VALUE);
se.setTimeStamp(100);
//
Serializable pk = storage.saveStreamElement(se);
assertNotNull(pk);
StreamElement seOut = storage.getStreamElement(pk);
assertNotNull(seOut);
//
for (DataField df : structure) {
if (!removed.getName().equalsIgnoreCase(df.getName()))
assertNotNull(seOut.getData(df.getName()));
else
assertNull(seOut.getData(df.getName()));
}
// Check that the values are equals
for (int i = 0; i < seOut.getData().length; i++) {
if (seOut.getFieldTypes()[i] != DataTypes.BINARY)
assertEquals(se.getData(seOut.getFieldNames()[i]), seOut.getData()[i]);
}
}
@Test
public void testCorrectInsertionWithUnorderedFields() {
DataField[] structure = dataField.toArray(new DataField[]{});
//
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, "testCorrectInsertionWithUnorderedFields", dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
// Build a StreamElement which reorder fields
DataField[] fields = dataField.toArray(new DataField[]{});
DataField tmp = fields[fields.length - 1];
fields[fields.length - 1] = fields[0];
fields[0] = tmp;
StreamElement se = generateStreamElement(fields, Byte.MIN_VALUE);
se.setTimeStamp(100);
//
Serializable pk = storage.saveStreamElement(se);
assertNotNull(pk);
StreamElement seOut = storage.getStreamElement(pk);
assertNotNull(seOut);
// Check that the values are equals
for (int i = 0; i < seOut.getData().length; i++) {
if (seOut.getFieldTypes()[i] != DataTypes.BINARY)
assertEquals(se.getData(seOut.getFieldNames()[i]), seOut.getData()[i]);
}
}
@Test
public void testCorrectInsertionWithNullValues() {
DataField[] structure = dataField.toArray(new DataField[]{});
//
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, "testCorrectInsertionWithNullValues", dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
// Build a StreamElement whith null values
DataField[] fields = dataField.toArray(new DataField[]{});
StreamElement se = generateStreamElement(fields, Byte.MIN_VALUE);
se.setTimeStamp(100);
for (int i = 0; i < se.getData().length; i++) {
se.setData(i, null);
}
//
Serializable pk = storage.saveStreamElement(se);
assertNotNull(pk);
StreamElement seOut = storage.getStreamElement(pk);
assertNotNull(seOut);
// Check that the values are equals
for (int i = 0; i < seOut.getData().length; i++) {
if (seOut.getFieldTypes()[i] != DataTypes.BINARY)
assertEquals(se.getData(seOut.getFieldNames()[i]), seOut.getData()[i]);
}
}
@Test(expected = ch.epfl.gsn.utils.GSNRuntimeException.class)
public void testWrongInsertDuToUniqueTimed() {
DataField[] structure = dataField.toArray(new DataField[]{});
//
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, "testWrongInsertDuToUniqueTimed", dataField.toArray(new DataField[]{}), true);
assertNotNull(storage);
// Build two StreamElement with the same timed field
StreamElement se1 = generateStreamElement(structure, Byte.MIN_VALUE);
se1.setTimeStamp(100);
StreamElement se2 = generateStreamElement(structure, Byte.MAX_VALUE);
se2.setTimeStamp(100);
// Store the first stream element, should work
Serializable pk = storage.saveStreamElement(se1);
assertNotNull(pk);
// Store the second stream element, should throw an exception because of the duplicated timed field.
pk = storage.saveStreamElement(se2);
}
@Test(expected = ch.epfl.gsn.utils.GSNRuntimeException.class)
public void testWrongInsertDueToFormatTypeMismatch() {
DataField[] structure = dataField.toArray(new DataField[]{});
//
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, "testWrongInsertDueToFormatTypeMismatch", dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
// Build a StreamElement which change a field type compared to the structure.
String oldName = dataField.get(0).getName();
byte oldType = dataField.get(0).getDataTypeID();
byte wrongType = DataTypes.INTEGER;
//
assertNotSame(wrongType, dataField.get(0).getDataTypeID()); // Check that we are seting a different type.
dataField.set(0, new DataField(oldName, wrongType));
assertEquals(structure.length, dataField.size()); // Check that the number of element match
StreamElement se = generateStreamElement(dataField.toArray(new DataField[]{}), Byte.MIN_VALUE);
se.setTimeStamp(100);
// Should generate an exception because the StreamElement structure does not match the storage structure.
Serializable pk = storage.saveStreamElement(se);
}
@Test
public void testConcurrentInsertion() {
final int nbThread = 10;
final long nbStreamElement = 100;
//
final DataField[] structure = dataField.toArray(new DataField[]{});
final HibernateStorage storage = HibernateStorage.newInstance(dbInfo, "testConcurrentInsertion", dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
//
ArrayList<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < nbThread; i++) {
final int j = i;
threads.add(new Thread() {
int tid = j;
public void run() {
System.out.println("Thread " + tid + " has started.");
for (int k = 0; k < nbStreamElement; k++) {
// Build a StreamElement to store
StreamElement se1 = generateStreamElement(structure, Byte.MIN_VALUE);
se1.setTimeStamp(System.currentTimeMillis());
Serializable pk = storage.saveStreamElement(se1);
assertNotNull(pk);
}
}
});
}
for (Thread thread : threads) {
thread.start();
}
// Wait that all the thread have completed
for (Thread thread : threads) {
try {
thread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
//Check that the number of elements euqlas: nbThread x nbStreamElement.
assertEquals((nbThread * nbStreamElement), storage.countStreamElement());
}
@Test
public void testPaginatedQueryWithoutCriterionWithLimit() {
int numberOfElements = 283;
String identifier = "testPaginatedQueryWithoutCriterionWithLimit";
generateDataTest(identifier, numberOfElements);
//
DataField[] structure = dataField.toArray(new DataField[]{});
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, identifier, dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
//
int[] pageSizes = new int[]{1, 11, numberOfElements / 2, numberOfElements / 3, numberOfElements / 3 - 1, numberOfElements / 3 + 1, numberOfElements - 1, numberOfElements + 1, numberOfElements * 2};
//
for (int pageSize : pageSizes) {
System.out.println("testPaginatedQueryWithoutCriterionWithLimit ASC with pageSize: " + pageSize);
// Set the limit to a fixed number: 1
checkQueryResult(storage.getStreamElements(pageSize, Order.asc("timed"), new Criterion[]{}, 1), 1, 1);
// Set the limit to a fixed number: 11
checkQueryResult(storage.getStreamElements(pageSize, Order.asc("timed"), new Criterion[]{}, 11), 1, 11);
// Set the limit to numberOfElements/10+1
checkQueryResult(storage.getStreamElements(pageSize, Order.asc("timed"), new Criterion[]{}, numberOfElements / 10 + 1), 1, numberOfElements / 10 + 1);
// Set the limit to numberOfElements/10-1
checkQueryResult(storage.getStreamElements(pageSize, Order.asc("timed"), new Criterion[]{}, numberOfElements / 10 - 1), 1, numberOfElements / 10 - 1);
// Set the limit to 0
checkQueryResult(storage.getStreamElements(pageSize, Order.asc("timed"), new Criterion[]{}, 0), -1, -1);
}
//
for (int pageSize : pageSizes) {
System.out.println("testPaginatedQueryWithoutCriterionWithLimit DESC with pageSize: " + pageSize);
// Set the limit to a fixed number: 1
checkQueryResult(storage.getStreamElements(pageSize, Order.desc("timed"), new Criterion[]{}, 1), numberOfElements, numberOfElements);
// Set the limit to a fixed number: 11
checkQueryResult(storage.getStreamElements(pageSize, Order.desc("timed"), new Criterion[]{}, 11), numberOfElements, numberOfElements - 10);
// Set the limit to numberOfElements/10+1
checkQueryResult(storage.getStreamElements(pageSize, Order.desc("timed"), new Criterion[]{}, numberOfElements / 10 + 1), numberOfElements, numberOfElements - (numberOfElements / 10 + 1) + 1);
// Set the limit to numberOfElements/10-1
checkQueryResult(storage.getStreamElements(pageSize, Order.desc("timed"), new Criterion[]{}, numberOfElements / 10 - 1), numberOfElements, numberOfElements - (numberOfElements / 10 - 1) + 1);
// Set the limit to 0
checkQueryResult(storage.getStreamElements(pageSize, Order.desc("timed"), new Criterion[]{}, 0), -1, -1);
}
}
@Test
public void testPaginatedQueryWitCriterionWithLimit() {
int numberOfElements = 817;
String identifier = "testPaginatedQueryWitCriterionWithLimit";
generateDataTest(identifier, numberOfElements);
//
DataField[] structure = dataField.toArray(new DataField[]{});
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, identifier, dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
//
int[] pageSizes = new int[]{1, 11, numberOfElements / 2, numberOfElements / 3, numberOfElements / 3 - 1, numberOfElements / 3 + 1, numberOfElements - 1, numberOfElements + 1, numberOfElements * 2};
//
for (int pageSize : pageSizes) {
System.out.println("testPaginatedQueryWitCriterionWithLimit with pageSize: " + pageSize);
// Set the limit to a fixed number: 1 and timed > numberOfElements/2
checkQueryResult(storage.getStreamElements(pageSize, Order.asc("timed"), new Criterion[]{Restrictions.gt("timed", (long)numberOfElements/2)}, 1), numberOfElements/2+1, numberOfElements/2+1);
// Set the limit to a fixed number: 11 and timed > numberOfElements/2
checkQueryResult(storage.getStreamElements(pageSize, Order.asc("timed"), new Criterion[]{Restrictions.gt("timed", (long)numberOfElements/2)}, 11), numberOfElements/2+1, numberOfElements/2+11);
// Set the limit to 0 and timed > numberOfElements/2
checkQueryResult(storage.getStreamElements(pageSize, Order.asc("timed"), new Criterion[]{Restrictions.gt("timed", (long)numberOfElements/2)}, 0), -1, -1);
}
}
private void checkQueryResult(DataEnumeratorIF de, int firstTimed, int lastTimed) {
System.out.println("Checking Query Result with expected firstTimed: " + firstTimed + ", expected lastTimed: " + lastTimed);
assertNotNull(de);
int nb = 0;
if (lastTimed < 0 || firstTimed < 0) {
while (de.hasMoreElements()) {
StreamElement se = de.nextElement();
assertNotNull(se);
nb++;
}
assertEquals(0, nb);
} else {
if (firstTimed <= lastTimed) { // ASC
while (de.hasMoreElements()) {
StreamElement se = de.nextElement();
assertNotNull(se);
assertEquals(firstTimed + nb, (int) se.getTimeStamp());
nb++;
}
assertEquals((lastTimed - firstTimed + 1), nb);
} else { // DESC
while (de.hasMoreElements()) {
StreamElement se = de.nextElement();
assertNotNull(se);
assertEquals(firstTimed - nb, (int) se.getTimeStamp());
nb++;
}
assertEquals((firstTimed - lastTimed + 1), nb);
}
}
}
//
/**
* @param fields
* @param mode mode < 0 : return a StreamElement with the minimal value for each field
* mode >= 0 : return a StreamElement with the maximal value for each field
* @return
*/
private StreamElement generateStreamElement(DataField[] fields, byte mode) {
ArrayList<Serializable> data = new ArrayList<Serializable>();
for (DataField df : fields) {
switch (df.getDataTypeID()) {
case DataTypes.VARCHAR:
case DataTypes.CHAR:
data.add("A message.");
break;
case DataTypes.INTEGER:
data.add(mode < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE);
break;
case DataTypes.BIGINT:
data.add(mode < 0 ? Long.MIN_VALUE : Long.MAX_VALUE);
break;
case DataTypes.BINARY:
data.add(new byte[]{0x01, 0x02, 0x03});
break;
case DataTypes.DOUBLE:
data.add(mode < 0 ? -1000000000000.0 : 1000000000000.0); // We don't use the Double.MIN & Double.MAX as the DBMS round the values and thus change them to -inf or +inf
break;
case DataTypes.TIME:
data.add(System.currentTimeMillis());
break;
case DataTypes.TINYINT:
data.add(mode < 0 ? Byte.MIN_VALUE : Byte.MAX_VALUE);
break;
case DataTypes.SMALLINT:
data.add(mode < 0 ? Short.MIN_VALUE : Short.MAX_VALUE);
break;
}
}
return new StreamElement(fields, data.toArray(new Serializable[]{}));
}
private void generateDataTest(String identifier, int nb) {
DataField[] structure = dataField.toArray(new DataField[]{});
HibernateStorage storage = HibernateStorage.newInstance(dbInfo, identifier, dataField.toArray(new DataField[]{}), false);
assertNotNull(storage);
//
for (int k = 1; k <= nb; k++) {
// Build a StreamElement to store
StreamElement se1 = generateStreamElement(structure, Byte.MIN_VALUE);
se1.setTimeStamp(k);
Serializable pk = storage.saveStreamElement(se1);
assertNotNull(pk);
}
//Check the number of elements
assertEquals((long) nb, storage.countStreamElement());
}
}