/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* 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 org.neo4j.driver.internal;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.lang.reflect.Method;
import org.neo4j.driver.internal.retry.FixedRetryLogic;
import org.neo4j.driver.internal.spi.ConnectionProvider;
import org.neo4j.driver.internal.spi.PooledConnection;
import org.neo4j.driver.v1.AccessMode;
import org.neo4j.driver.v1.Logger;
import org.neo4j.driver.v1.Logging;
import org.neo4j.driver.v1.Session;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.neo4j.driver.v1.AccessMode.READ;
public class LeakLoggingNetworkSessionTest
{
@Rule
public final TestName testName = new TestName();
@Test
public void logsNothingDuringFinalizationIfClosed() throws Exception
{
Logging logging = mock( Logging.class );
Logger log = mock( Logger.class );
when( logging.getLog( anyString() ) ).thenReturn( log );
LeakLoggingNetworkSession session = newSession( logging, false );
finalize( session );
verify( log, never() ).error( anyString(), any( Throwable.class ) );
}
@Test
public void logsMessageWithStacktraceDuringFinalizationIfLeaked() throws Exception
{
Logging logging = mock( Logging.class );
Logger log = mock( Logger.class );
when( logging.getLog( anyString() ) ).thenReturn( log );
LeakLoggingNetworkSession session = newSession( logging, true );
// begin transaction to make session obtain a connection
session.beginTransaction();
finalize( session );
ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass( String.class );
verify( log ).error( messageCaptor.capture(), any( Throwable.class ) );
assertEquals( 1, messageCaptor.getAllValues().size() );
String loggedMessage = messageCaptor.getValue();
assertThat( loggedMessage, containsString( "Neo4j Session object leaked" ) );
assertThat( loggedMessage, containsString( "Session was create at" ) );
assertThat( loggedMessage, containsString(
getClass().getSimpleName() + "." + testName.getMethodName() )
);
}
private static void finalize( Session session ) throws Exception
{
Method finalizeMethod = session.getClass().getDeclaredMethod( "finalize" );
finalizeMethod.setAccessible( true );
finalizeMethod.invoke( session );
}
private static LeakLoggingNetworkSession newSession( Logging logging, boolean openConnection )
{
return new LeakLoggingNetworkSession( connectionProviderMock( openConnection ), READ,
new FixedRetryLogic( 0 ), logging );
}
private static ConnectionProvider connectionProviderMock( final boolean openConnection )
{
ConnectionProvider provider = mock( ConnectionProvider.class );
when( provider.acquireConnection( any( AccessMode.class ) ) ).thenAnswer( new Answer<PooledConnection>()
{
@Override
public PooledConnection answer( InvocationOnMock invocation ) throws Throwable
{
return connectionMock( openConnection );
}
} );
return provider;
}
private static PooledConnection connectionMock( boolean open )
{
PooledConnection connection = mock( PooledConnection.class );
when( connection.isOpen() ).thenReturn( open );
return connection;
}
}