/**
* Copyright (c) 2002-2010 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.ext.udc.impl;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.apache.http.localserver.LocalTestServer;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.kernel.EmbeddedGraphDatabase;
/**
* Unit testing for the UDC kernel extension.
*
* The UdcExtensionImpl is loaded when a new
* GraphDatabase is instantiated, as part of
* {@link org.neo4j.helpers.Service#load}.
*
*/
public class UdcExtensionImplTest {
Random rnd = new Random();
@Before
public void resetUdcState() {
UdcTimerTask.successCounts.clear();
UdcTimerTask.failureCounts.clear();
}
/**
* Sanity check to make sure a database can be created
* and destroyed.
* @throws java.io.IOException
*/
@Test
public void shouldNotCrashNormalGraphdbCreation() throws IOException {
EmbeddedGraphDatabase graphdb = createTempDatabase(null);
destroy(graphdb);
}
/**
* Expect the counts to be initialized.
*/
@Test
public void shouldLoadWhenNormalGraphdbIsCreated() throws Exception {
EmbeddedGraphDatabase graphdb = createTempDatabase(null);
// when the UDC extension successfully loads, it initializes the attempts count to 0
assertGotSuccessWithRetry( IS_ZERO );
destroy(graphdb);
}
/**
* Expect separate counts for each graphdb.
*/
@Test
public void shouldLoadForEachCreatedGraphdb() throws IOException {
EmbeddedGraphDatabase graphdb1 = createTempDatabase(null);
EmbeddedGraphDatabase graphdb2 = createTempDatabase(null);
Set<String> successCountValues = UdcTimerTask.successCounts.keySet();
assertThat(successCountValues.size(), equalTo(2));
assertThat("this", is(not("that")));
destroy(graphdb1);
destroy(graphdb2);
}
@Test
public void shouldRecordFailuresWhenThereIsNoServer() throws Exception {
Map<String, String> config = new HashMap<String, String>();
config.put(UdcExtensionImpl.FIRST_DELAY_CONFIG_KEY, "100"); // first delay must be long enough to allow class initialization to complete
config.put(UdcExtensionImpl.UDC_HOST_ADDRESS_KEY, "127.0.0.1:1"); // first delay must be long enough to allow class initialization to complete
EmbeddedGraphDatabase graphdb = new EmbeddedGraphDatabase("should-record-failures", config);
assertGotFailureWithRetry( IS_GREATER_THAN_ZERO );
destroy(graphdb);
}
@Test
public void shouldRecordSuccessesWhenThereIsAServer() throws Exception {
// first, set up the test server
LocalTestServer server = new LocalTestServer(null, null);
PingerHandler handler = new PingerHandler();
server.register("/*", handler);
server.start();
final String hostname = server.getServiceHostName();
final String serverAddress = hostname + ":" + server.getServicePort();
Map<String, String> config = new HashMap<String, String>();
config.put(UdcExtensionImpl.FIRST_DELAY_CONFIG_KEY, "100");
config.put(UdcExtensionImpl.UDC_HOST_ADDRESS_KEY, serverAddress);
EmbeddedGraphDatabase graphdb = createTempDatabase(config);
assertGotSuccessWithRetry( IS_GREATER_THAN_ZERO );
assertGotFailureWithRetry( IS_ZERO );
destroy(graphdb);
}
private static interface Condition<T>
{
boolean isTrue( T value );
}
private static final Condition<Integer> IS_ZERO = new Condition<Integer>()
{
public boolean isTrue( Integer value )
{
return value == 0;
}
};
private static final Condition<Integer> IS_GREATER_THAN_ZERO = new Condition<Integer>()
{
public boolean isTrue( Integer value )
{
return value > 0;
}
};
private void assertGotSuccessWithRetry( Condition<Integer> condition ) throws Exception
{
assertGotPingWithRetry( UdcTimerTask.successCounts, condition );
}
private void assertGotFailureWithRetry( Condition<Integer> condition ) throws Exception
{
assertGotPingWithRetry( UdcTimerTask.failureCounts, condition );
}
private void assertGotPingWithRetry( Map<String, Integer> counts, Condition<Integer> condition ) throws Exception
{
for ( int i = 0; i < 10; i++ )
{
Thread.sleep(200);
Collection<Integer> countValues = counts.values();
Integer count = countValues.iterator().next();
if ( condition.isTrue( count ) )
{
return;
}
}
fail();
}
private EmbeddedGraphDatabase createTempDatabase(Map<String,String> config) throws IOException {
EmbeddedGraphDatabase tempdb = null;
String randomDbName = "tmpdb-" + rnd.nextInt();
File possibleDirectory = new File(randomDbName);
if (possibleDirectory.exists()) {
FileUtils.deleteDirectory(possibleDirectory);
}
if (config == null) {
tempdb = new EmbeddedGraphDatabase(randomDbName);
} else {
tempdb = new EmbeddedGraphDatabase(randomDbName, config);
}
return tempdb;
}
private void destroy(EmbeddedGraphDatabase dbToDestroy) throws IOException {
dbToDestroy.shutdown();
FileUtils.deleteDirectory(new File(dbToDestroy.getStoreDir()));
}
}