/*
* Copyright 2010 The Rabbit Eclipse Plug-in 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 rabbit.data.internal.xml.store;
import static rabbit.data.internal.xml.DatatypeUtil.toXmlDate;
import rabbit.data.internal.xml.DatatypeUtil;
import rabbit.data.internal.xml.IDataStore;
import rabbit.data.internal.xml.XmlPlugin;
import rabbit.data.internal.xml.convert.IConverter;
import rabbit.data.internal.xml.merge.IMerger;
import rabbit.data.internal.xml.schema.events.EventGroupType;
import rabbit.data.internal.xml.schema.events.EventListType;
import rabbit.data.store.model.DiscreteEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.eclipse.core.runtime.IPath;
import org.joda.time.DateTime;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.xml.datatype.XMLGregorianCalendar;
/**
* @see AbstractStorer
*/
public abstract class AbstractStorerTest<E extends DiscreteEvent, T, S extends EventGroupType> {
/**
* Location of the storage path, to be restored after the tests.
*/
private static IPath originalStorageLocation;
@AfterClass
public static void afterClass() {
XmlPlugin.getDefault().setStoragePathRoot(originalStorageLocation.toFile());
}
@BeforeClass
public static void beforeClass() {
originalStorageLocation = XmlPlugin.getDefault().getStoragePathRoot();
XmlPlugin.getDefault().setStoragePathRoot(
originalStorageLocation.append("Tests").toFile());
}
private AbstractStorer<E, T, S> storer = createStorer();
@Before
public void before() throws Exception {
getDataField(storer).clear();
}
@Test
public void testCommit() throws Exception {
E event = createEvent(new DateTime());
storer.insert(event);
File file = getDataStore(storer).getDataFile(event.getTime().toLocalDate());
if (file.exists() && !file.delete())
fail("File must be deleted before test can continue");
storer.commit();
List<S> data = getCategories(storer, getDataStore(storer).read(file));
assertEquals(1, data.size());
assertEquals(toXmlDate(event.getTime()), data.get(0).getDate());
List<T> elements = storer.getElements(data.get(0));
assertEquals(1, elements.size());
T element = elements.get(0);
assertTrue(equal(getConverter(storer).convert(event), element));
}
@Test
public void testCommit_emptyDataAfterward() throws Exception {
E event = createEvent(new DateTime());
storer.insert(event);
assertFalse(getDataField(storer).isEmpty());
storer.commit();
assertTrue(getDataField(storer).isEmpty());
}
@Test
public void testGetCategories() throws Exception {
assertNotNull(getCategories(storer, new EventListType()));
}
@Test
public void testGetConverter() throws Exception {
assertNotNull(getConverter(storer));
}
@Test
public void testGetDataStore() throws Exception {
assertNotNull(getDataStore(storer));
}
@Test
public void testInsert_singleElement() throws Exception {
E event = createEvent(new DateTime());
storer.insert(event);
Collection<S> categories = getDataField(storer);
assertEquals(1, categories.size());
S category = categories.iterator().next();
assertEquals(toXmlDate(event.getTime()), category.getDate());
List<T> elements = storer.getElements(category);
assertEquals(1, elements.size());
T element = elements.iterator().next();
assertTrue(equal(getConverter(storer).convert(event), element));
}
@Test
public void testInsert_withConvertableElements() throws Exception {
E event1 = createEvent(new DateTime());
E event2 = createEvent(event1.getTime());
storer.insert(event1);
storer.insert(event2);
Collection<S> categories = getDataField(storer);
assertEquals(1, categories.size());
S category = categories.iterator().next();
assertEquals(toXmlDate(event1.getTime()), category.getDate());
List<T> elements = storer.getElements(category);
T t1 = getConverter(storer).convert(event1);
T t2 = getConverter(storer).convert(event2);
IMerger<T> merger = storer.getMerger();
if (merger != null) { // Elements are mergeable
assertEquals(1, elements.size());
T element = elements.iterator().next();
assertTrue(equal(merger.merge(t1, t2), element));
} else { // Elements are not mergeable
assertEquals(2, elements.size());
Iterator<T> iterator = elements.iterator();
// It doesn't matter what order we check, they are all equal, because
// the two events are created equally:
assertTrue(equal(t1, iterator.next()));
assertTrue(equal(t2, iterator.next()));
}
}
@Test
public void testInsert_withElementsInDifferentDates() throws Exception {
E event1 = createEvent(new DateTime().withDayOfYear(1));
E event2 = createEventDiff(event1.getTime().plusDays(1));
storer.insert(event1);
storer.insert(event2);
Collection<S> categories = getDataField(storer);
assertEquals(2, categories.size());
Iterator<S> iterator = categories.iterator();
S category1 = iterator.next();
S category2 = iterator.next();
// Swap the categories if neccessary so that category1 contains event1 etc.
if (category1.getDate().equals(toXmlDate(event2.getTime()))) {
S tmp = category1;
category1 = category2;
category2 = tmp;
}
assertEquals(toXmlDate(event1.getTime()), category1.getDate());
assertEquals(toXmlDate(event2.getTime()), category2.getDate());
List<T> elements = storer.getElements(category1);
assertEquals(1, elements.size());
assertTrue(equal(getConverter(storer).convert(event1), elements.get(0)));
elements = storer.getElements(category2);
assertEquals(1, elements.size());
assertTrue(equal(getConverter(storer).convert(event2), elements.get(0)));
}
@Test
public void testInsert_withElementsInDifferentMonths() throws Exception {
E event1 = createEvent(new DateTime());
E event2 = createEvent(event1.getTime().plusMonths(1));
storer.insert(event1);
storer.insert(event2);
// Now data about event1 should be commit, so only data about event2 is in
// memory.
Collection<S> categories = getDataField(storer);
assertEquals(1, categories.size());
S category = categories.iterator().next();
assertEquals(toXmlDate(event2.getTime()), category.getDate());
List<T> elements = storer.getElements(category);
assertEquals(1, elements.size());
T element = elements.iterator().next();
assertTrue(equal(getConverter(storer).convert(event2), element));
}
@Test
public void testInsert_withNonConvertableElements() throws Exception {
E event1 = createEvent(new DateTime());
E event2 = createEventDiff(event1.getTime());
storer.insert(event1);
storer.insert(event2);
Collection<S> categories = getDataField(storer);
assertEquals(1, categories.size());
S category = categories.iterator().next();
assertEquals(toXmlDate(event1.getTime()), category.getDate());
List<T> elements = storer.getElements(category);
assertEquals(2, elements.size());
T t1 = getConverter(storer).convert(event1);
T t2 = getConverter(storer).convert(event2);
assertTrue(equal(t1, elements.get(0)));
assertTrue(equal(t2, elements.get(1)));
}
@Test
public void testNewCateogry() throws Exception {
XMLGregorianCalendar date = newCalendar();
S category = storer.newCategory(date);
assertNotNull(category);
assertEquals(date, category.getDate());
}
/**
* Creates an event for testing. Subsequence calls to this method should
* return equal objects.
*/
protected abstract E createEvent(DateTime dateTime) throws Exception;
/**
* Creates an event for testing. The return event must be different to
* {@link #createEvent(DateTime)} (Non mergeable). Subsequence calls to this
* method should return equal objects.
*/
protected abstract E createEventDiff(DateTime dateTime) throws Exception;
/**
* Creates a storer for testing.
*/
protected abstract AbstractStorer<E, T, S> createStorer();
/**
* Checks whether the two objects have the exact same properties.
*/
protected abstract boolean equal(T t1, T t2);
/**
* Calls the protected method
* {@code AbstractStorer.getXmlTypeCategories(EventListType)}.
*/
@SuppressWarnings("unchecked")
protected List<S> getCategories(AbstractStorer<E, T, S> storer,
EventListType events) throws Exception {
Method method = AbstractStorer.class.getDeclaredMethod("getCategories",
EventListType.class);
method.setAccessible(true);
return (List<S>) method.invoke(storer, events);
}
/**
* Calls the protected method AbstractStorer.getConverter()
*/
@SuppressWarnings("unchecked")
protected IConverter<E, T> getConverter(AbstractStorer<E, T, S> storer)
throws Exception {
Method method = AbstractStorer.class.getDeclaredMethod("getConverter");
method.setAccessible(true);
return (IConverter<E, T>) method.invoke(storer);
}
@SuppressWarnings("unchecked")
protected Collection<S> getDataField(AbstractStorer<E, T, S> s)
throws Exception {
Field f = AbstractStorer.class.getDeclaredField("data");
f.setAccessible(true);
return (Collection<S>) f.get(s);
}
/**
* Calls the protected method {@code AbstractStorer.getDataStore()}.
*/
protected IDataStore getDataStore(AbstractStorer<E, T, S> storer)
throws Exception {
Method method = AbstractStorer.class.getDeclaredMethod("getDataStore");
method.setAccessible(true);
return (IDataStore) method.invoke(storer);
}
private XMLGregorianCalendar newCalendar() {
return DatatypeUtil.toXmlDate(new DateTime());
}
}