package at.punkt.lod2.local;
import at.punkt.lod2.util.Helper;
import com.jayway.awaitility.Awaitility;
import eu.lod2.rsine.dissemination.messageformatting.ToStringBindingSetFormatter;
import eu.lod2.rsine.dissemination.notifier.INotifier;
import eu.lod2.rsine.queryhandling.PostponedQueryHandler;
import eu.lod2.rsine.queryhandling.QueryEvaluator;
import eu.lod2.rsine.registrationservice.NotificationQuery;
import eu.lod2.rsine.registrationservice.RegistrationService;
import eu.lod2.rsine.registrationservice.Subscription;
import eu.lod2.rsine.service.ChangeTripleService;
import eu.lod2.rsine.service.PersistAndNotifyProvider;
import eu.lod2.util.Namespaces;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openrdf.model.impl.LiteralImpl;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.repository.RepositoryException;
import org.openrdf.rio.RDFParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"MinTimePassedEvaluationPolicyTest-context.xml"})
@DirtiesContext
public class MinTimePassedEvaluationPolicyTest {
private final long IMMEDIATE_NOTIFICATION_THRESHOLD_MILLIS = 1000;
private TimeMeasureNotifier timeMeasureNotifier = new TimeMeasureNotifier();
@Autowired
private PersistAndNotifyProvider persistAndNotifyProvider;
@Autowired
private RegistrationService registrationService;
@Autowired
private PostponedQueryHandler postponedQueryHandler;
@Before
public void setUp() throws IOException, RepositoryException, RDFParseException, QueryEvaluationException, MalformedQueryException {
Subscription subscription = new Subscription();
subscription.addQuery(new NotificationQuery(createQuery(), new ToStringBindingSetFormatter(), subscription));
subscription.addNotifier(timeMeasureNotifier);
registrationService.register(subscription, true);
}
private String createQuery() {
//preflabel changes of concepts
return Namespaces.SKOS_PREFIX+
Namespaces.CS_PREFIX+
Namespaces.DCTERMS_PREFIX+
"SELECT * " +
"WHERE {" +
"?cs a cs:ChangeSet . " +
"?cs cs:createdDate ?csdate . " +
"?cs cs:removal ?removal . " +
"?cs cs:addition ?addition . " +
"?removal rdf:subject ?concept . " +
"?addition rdf:subject ?concept . " +
"?removal rdf:predicate skos:prefLabel . " +
"?removal rdf:object ?oldLabel . "+
"?addition rdf:predicate skos:prefLabel . " +
"?addition rdf:object ?newLabel . "+
"FILTER (?csdate > \"" + QueryEvaluator.QUERY_LAST_ISSUED+ "\"^^<http://www.w3.org/2001/XMLSchema#dateTime>)" +
"}";
}
protected void changePrefLabel() throws IOException {
persistAndNotifyProvider.persistAndNotify(
Helper.createChangeSetModel("http://reegle.info/glossary/1111",
"http://www.w3.org/2004/02/skos/core#prefLabel",
new LiteralImpl("Ottakringer Helles", "en"),
"http://reegle.info/glossary/1111",
"http://www.w3.org/2004/02/skos/core#prefLabel",
new LiteralImpl("Schremser Edelmärzen", "en"),
ChangeTripleService.CHANGETYPE_UPDATE),
true);
}
@Test
public void immediateNotificationOnFirstChange() throws IOException {
performChange();
Awaitility.await().atMost(2, TimeUnit.SECONDS).until(new NotificationDetector(timeMeasureNotifier));
Assert.assertTrue(timeMeasureNotifier.millisPassed < IMMEDIATE_NOTIFICATION_THRESHOLD_MILLIS);
}
private void performChange() throws IOException {
timeMeasureNotifier.reset();
changePrefLabel();
}
@Test
public void changeTooSoonForNotification() throws IOException {
performChange();
Awaitility.await().atMost(2, TimeUnit.SECONDS).until(new NotificationDetector(timeMeasureNotifier));
performChange();
Awaitility.await().atMost(10, TimeUnit.SECONDS).until(new NotificationDetector(timeMeasureNotifier));
Assert.assertTrue(timeMeasureNotifier.getMillisPassed() > IMMEDIATE_NOTIFICATION_THRESHOLD_MILLIS);
}
@Test
public void changeLateEnoughForNotification() throws IOException {
performChange();
Awaitility.await().atMost(2, TimeUnit.SECONDS).until(new NotificationDetector(timeMeasureNotifier));
try {
Thread.sleep(6000);
}
catch (InterruptedException e) {
// ignore
}
performChange();
Awaitility.await().atMost(2, TimeUnit.SECONDS).until(new NotificationDetector(timeMeasureNotifier));
}
@Test
public void lastChangeNotMissed() throws IOException {
performChange();
Awaitility.await().atMost(2, TimeUnit.SECONDS).until(new NotificationDetector(timeMeasureNotifier));
performChange();
Awaitility.await().atMost(20, TimeUnit.SECONDS).until(new NotificationDetector(timeMeasureNotifier));
}
private class NotificationDetector implements Callable<Boolean> {
private TimeMeasureNotifier notifier;
private NotificationDetector(TimeMeasureNotifier notifier) {
this.notifier = notifier;
}
@Override
public Boolean call() throws Exception {
return notifier.getMillisPassed() != null;
}
}
private class TimeMeasureNotifier implements INotifier {
private long time = 0;
private Long millisPassed = null;
private void reset() {
millisPassed = null;
time = System.currentTimeMillis();
}
@Override
public synchronized void notify(Collection<String> messages) {
millisPassed = System.currentTimeMillis() - time;
}
public synchronized Long getMillisPassed() {
return millisPassed;
}
}
}