/** * Copyright (c) 2002-2011 "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 org.apache.commons.io.FileUtils; import org.apache.http.localserver.LocalTestServer; import org.junit.Before; import org.junit.Test; import org.neo4j.kernel.EmbeddedGraphDatabase; 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 static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** * Unit testing for the UDC kernel extension. * <p/> * 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" ); 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 ); } @Test public void shouldBeAbleToSpecifySourceWithConfig() 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 ); config.put( UdcExtensionImpl.UDC_SOURCE_KEY, "test" ); EmbeddedGraphDatabase graphdb = createTempDatabase( config ); assertGotSuccessWithRetry( IS_GREATER_THAN_ZERO ); assertEquals( "test", handler.getQueryMap().get( "source" ) ); 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( "target" + File.separator + 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() ) ); } }