/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.jena.graph;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.mockito.Mockito;
import org.xenei.junit.contract.Contract;
import org.xenei.junit.contract.ContractTest;
import static org.junit.Assert.*;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.GraphEventManager;
import org.apache.jena.graph.Triple;
import org.apache.jena.testing_framework.GraphHelper;
import org.xenei.junit.contract.IProducer;
import org.apache.jena.testing_framework.NodeCreateUtils;
import static org.apache.jena.testing_framework.GraphHelper.*;
/**
* An abstract test that tests EventManager implementations to ensure they
* comply with the EventManager contract.
*
* Subclasses of this class must implement the getEventManagerProducer() method
* to create a new instance of the EventManager for testing.
*/
@Contract(GraphEventManager.class)
public class GraphEventManagerContractTest<T extends GraphEventManager> {
protected static final Triple[] tripleArray = tripleArray("S P O; Foo R B; X Q Y");
private IProducer<T> producer;
// the graph that is used as the source of the events.
private Graph mockGraph;
// The event manager we are working with.
private GraphEventManager gem;
public GraphEventManagerContractTest() {
}
@Contract.Inject
public final void setGraphEventManagerContractTestProducer(
IProducer<T> producer) {
this.producer= producer;
}
@After
public final void afterGraphEventManagerContractTest() {
producer.cleanUp();
}
@Before
public final void beforeGraphEventManagerContractTest() {
mockGraph = Mockito.mock(Graph.class);
gem = producer.newInstance();
L = new RecordingGraphListener();
}
/**
* Test that when a listener is registered the same EventManager is
* returned.
*/
@ContractTest
public void testEventRegister() {
assertSame(gem, gem.register(new RecordingGraphListener()));
}
/**
* Test that we can safely unregister a listener that isn't registered.
*/
@ContractTest
public void testEventUnregister() {
assertSame(gem, gem.unregister(L));
}
/**
* Handy triple for test purposes.
*/
protected Triple SPO = NodeCreateUtils.createTriple("S P O");
/**
* The RecordingListener that reports all the events sent from the
* EventManager.
*/
protected RecordingGraphListener L;
/**
* Test that adding a triple is reported.
*/
@ContractTest
public void testAddTriple() {
gem.register(L);
gem.notifyAddTriple(mockGraph, SPO);
L.assertHas(new Object[] { "add", mockGraph, SPO });
}
/**
* Test that deleting a triple is reported.
*/
@ContractTest
public void testDeleteTriple() {
gem.register(L);
gem.notifyDeleteTriple(mockGraph, SPO);
L.assertHas(new Object[] { "delete", mockGraph, SPO });
}
/**
* Test that when 2 listeners are added both receive events.
*/
@ContractTest
public void testTwoListeners() {
RecordingGraphListener L1 = new RecordingGraphListener();
RecordingGraphListener L2 = new RecordingGraphListener();
gem.register(L1).register(L2);
gem.notifyAddTriple(mockGraph, SPO);
L2.assertHas(new Object[] { "add", mockGraph, SPO });
L1.assertHas(new Object[] { "add", mockGraph, SPO });
}
/**
* Test that unregistering a listener after registering it results in it not
* receiving messages.
*/
@ContractTest
public void testUnregisterWorks() {
gem.register(L).unregister(L);
gem.notifyAddTriple(mockGraph, SPO);
L.assertHas(new Object[] {});
}
/**
* Test that registering a listener twice results in the listener receiving
* the events twice.
*/
@ContractTest
public void testRegisterTwice() {
gem.register(L).register(L);
gem.notifyAddTriple(mockGraph, SPO);
L.assertHas(new Object[] { "add", mockGraph, SPO, "add", mockGraph, SPO });
}
/**
* Test that registering a listener twice and unregistering it once will
* result in the listener receiving each event one time.
*/
@ContractTest
public void testUnregisterOnce() {
gem.register(L).register(L).unregister(L);
gem.notifyDeleteTriple(mockGraph, SPO);
L.assertHas(new Object[] { "delete", mockGraph, SPO });
}
/**
* Test that adding an array is reported as adding an array.
*/
@ContractTest
public void testAddArray() {
gem.register(L);
gem.notifyAddArray(mockGraph, tripleArray);
L.assertHas(new Object[] { "add[]", mockGraph, tripleArray });
}
/**
* Test that adding a list is reported as adding a list
*/
@ContractTest
public void testAddList() {
gem.register(L);
List<Triple> elems = Arrays.asList(tripleArray);
gem.notifyAddList(mockGraph, elems);
L.assertHas(new Object[] { "addList", mockGraph, elems });
}
/**
* Test that deleting an array is reported as deleting an array.
*/
@ContractTest
public void testDeleteArray() {
gem.register(L);
gem.notifyDeleteArray(mockGraph, tripleArray);
L.assertHas(new Object[] { "delete[]", mockGraph, tripleArray });
}
/**
* Test that deleting a list is reported as deleting a list.
*/
@ContractTest
public void testDeleteList() {
gem.register(L);
List<Triple> elems = Arrays.asList(tripleArray);
gem.notifyDeleteList(mockGraph, elems);
L.assertHas(new Object[] { "deleteList", mockGraph, elems });
}
/**
* Test that adding a list as an iterator is reported as an add iterator.
*/
@ContractTest
public void testAddListAsIterator() {
gem.register(L);
List<Triple> elems = Arrays.asList(tripleArray);
gem.notifyAddIterator(mockGraph, elems);
L.assertHas(new Object[] { "addIterator", mockGraph, elems });
}
/**
* Test that adding an iterator is reported as adding an iterator.
*/
@ContractTest
public void testAddIterator() {
gem.register(L);
List<Triple> elems = Arrays.asList(tripleArray);
gem.notifyAddIterator(mockGraph, elems.iterator());
L.assertHas(new Object[] { "addIterator", mockGraph, elems });
}
/**
* Test that deleting an iterator is reported as a deleting an iterator.
*/
@ContractTest
public void testDeleteIterator() {
gem.register(L);
List<Triple> elems = Arrays.asList(tripleArray);
gem.notifyDeleteIterator(mockGraph, elems.iterator());
L.assertHas(new Object[] { "deleteIterator", mockGraph, elems });
}
/**
* Test that deleting a list as an iterator is reported as deleting an
* iterator.
*/
@ContractTest
public void testDeleteListAsIterator() {
gem.register(L);
List<Triple> elems = Arrays.asList(tripleArray);
gem.notifyDeleteIterator(mockGraph, elems);
L.assertHas(new Object[] { "deleteIterator", mockGraph, elems });
}
/**
* Test that adding a graph is reported as adding a graph.
*/
@ContractTest
public void testAddGraph() {
gem.register(L);
Graph other = Mockito.mock(Graph.class);
gem.notifyAddGraph(mockGraph, other);
L.assertHas(new Object[] { "addGraph", mockGraph, other });
}
/**
* Test that deleting a graph is reported as deleting a graph.
*/
@ContractTest
public void testDeleteGraph() {
gem.register(L);
Graph other = Mockito.mock(Graph.class);
gem.notifyDeleteGraph(mockGraph, other);
L.assertHas(new Object[] { "deleteGraph", mockGraph, other });
}
/**
* Test that sending a general event is reported as an event and the value
* is saved.
*/
@ContractTest
public void testGeneralEvent() {
gem.register(L);
Object value = new int[] {};
gem.notifyEvent(mockGraph, value);
L.assertHas(new Object[] { "someEvent", mockGraph, value });
}
@ContractTest
public void testListening() {
assertFalse("Should not be listening", gem.listening());
gem.register(L);
assertTrue("Should be listening", gem.listening());
gem.unregister(L);
assertFalse("Should not be listening", gem.listening());
}
//
// Foo series of tests to check modifying the manger in mid notification
//
private ComeAndGoListener all[];
abstract private static class ComeAndGoListener implements GraphListener {
// Was I registered when start() was called, and have not been
// unregistered.
boolean inPlay = false;
// currently registered or not.
boolean registered = false;
boolean notified = false;
void register(GraphEventManager gem) {
registered = true;
gem.register(this);
}
void unregister(GraphEventManager gem) {
registered = false;
inPlay = false;
gem.unregister(this);
}
void start() {
if (registered)
inPlay = true;
}
void check() {
if (inPlay && !notified)
fail("listener that was in-play was not notified of adding triple.");
}
@Override
final public void notifyAddTriple(Graph g, Triple t) {
notified = true;
doSomeDamage();
}
abstract void doSomeDamage();
@Override
public void notifyAddArray(Graph g, Triple[] triples) {
}
@Override
public void notifyAddGraph(Graph g, Graph added) {
}
@Override
public void notifyAddIterator(Graph g, Iterator<Triple> it) {
}
@Override
public void notifyAddList(Graph g, List<Triple> triples) {
}
@Override
public void notifyDeleteArray(Graph g, Triple[] triples) {
}
@Override
public void notifyDeleteGraph(Graph g, Graph removed) {
}
@Override
public void notifyDeleteIterator(Graph g, Iterator<Triple> it) {
}
@Override
public void notifyDeleteList(Graph g, List<Triple> L) {
}
@Override
public void notifyDeleteTriple(Graph g, Triple t) {
}
@Override
public void notifyEvent(Graph source, Object value) {
}
}
/**
* ComeAndGoListener implementation that does no damage
*
*/
private static final class SimpleListener extends ComeAndGoListener {
@Override
void doSomeDamage() {
}
}
/**
* Test adding a triple to trigger event.
*
* @param registerTo
* Number of listeners to register.
* @param allx
*/
private void testAddingTriple(int registerTo, ComeAndGoListener... allx) {
all = allx;
// register addMe
for (int i = 0; i < registerTo; i++) {
all[i].register(gem);
}
// start them all
for (ComeAndGoListener l : all) {
l.start();
}
// send the notification.
gem.notifyAddTriple(mockGraph, GraphHelper.triple("make a change"));
// check them
for (ComeAndGoListener l : all) {
l.check();
}
}
/**
* Test that a listener added during event processing does not receive the
* event.
*/
@ContractTest
public void testAddOne() {
testAddingTriple(2, new ComeAndGoListener() {
@Override
void doSomeDamage() {
all[2].register(gem);
}
}, new SimpleListener(), new SimpleListener());
}
/**
* Test that when a listener that has not yet been handled is removed during
* event processing it receive the event.
*/
@ContractTest
public void testDelete2nd() {
testAddingTriple(3, new ComeAndGoListener() {
@Override
void doSomeDamage() {
all[1].unregister(gem);
}
}, new SimpleListener(), new SimpleListener());
}
/**
* Test that when a listener that has been handled is removed during event
* processing it receives the event.
*/
@ContractTest
public void testDelete1st() {
testAddingTriple(3, new SimpleListener(), new ComeAndGoListener() {
@Override
void doSomeDamage() {
all[0].unregister(gem);
}
}, new SimpleListener());
}
/**
* Test that when a listener that removes itself during event processing it
* receives the event.
*/
@ContractTest
public void testDeleteSelf() {
testAddingTriple(3, new ComeAndGoListener() {
@Override
void doSomeDamage() {
unregister(gem);
}
}, new SimpleListener(), new SimpleListener());
}
/**
* Test that when a listener that removes and adds itself during event
* processing it receives the event.
*/
@ContractTest
public void testDeleteAndAddSelf() {
testAddingTriple(3, new ComeAndGoListener() {
@Override
void doSomeDamage() {
unregister(gem);
register(gem);
}
}, new SimpleListener(), new SimpleListener());
}
}