/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2015 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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.pentaho.di.trans.steps.mock;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.pentaho.di.core.RowSet;
import org.pentaho.di.core.logging.KettleLogStore;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.logging.LogChannelInterfaceFactory;
import org.pentaho.di.core.logging.LogLevel;
import org.pentaho.di.core.logging.LogMessageInterface;
import org.pentaho.di.core.logging.LoggingObjectInterface;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.StepDataInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
public class StepMockHelper<Meta extends StepMetaInterface, Data extends StepDataInterface> {
public final StepMeta stepMeta;
public final Data stepDataInterface;
public final TransMeta transMeta;
public final Trans trans;
public final Meta initStepMetaInterface;
public final Data initStepDataInterface;
public final Meta processRowsStepMetaInterface;
public final Data processRowsStepDataInterface;
public final LogChannelInterface logChannelInterface;
public final LogChannelInterfaceFactory logChannelInterfaceFactory;
public final LogChannelInterfaceFactory originalLogChannelInterfaceFactory;
public StepMockHelper( String stepName, Class<Meta> stepMetaClass, Class<Data> stepDataClass ) {
originalLogChannelInterfaceFactory = KettleLogStore.getLogChannelInterfaceFactory();
logChannelInterfaceFactory = mock( LogChannelInterfaceFactory.class );
logChannelInterface = mock( LogChannelInterface.class );
KettleLogStore.setLogChannelInterfaceFactory( logChannelInterfaceFactory );
stepMeta = mock( StepMeta.class );
when( stepMeta.getName() ).thenReturn( stepName );
stepDataInterface = mock( stepDataClass );
transMeta = mock( TransMeta.class );
when( transMeta.findStep( stepName ) ).thenReturn( stepMeta );
trans = mock( Trans.class );
initStepMetaInterface = mock( stepMetaClass );
initStepDataInterface = mock( stepDataClass );
processRowsStepDataInterface = mock( stepDataClass );
processRowsStepMetaInterface = mock( stepMetaClass );
}
public RowSet getMockInputRowSet( Object[]... rows ) {
return getMockInputRowSet( asList( rows ) );
}
public RowSet getMockInputRowSet( final List<Object[]> rows ) {
final AtomicInteger index = new AtomicInteger( 0 );
RowSet rowSet = mock( RowSet.class, Mockito.RETURNS_MOCKS );
Answer<Object[]> answer = new Answer<Object[]>() {
@Override
public Object[] answer( InvocationOnMock invocation ) throws Throwable {
int i = index.getAndIncrement();
return i < rows.size() ? rows.get( i ) : null;
}
};
when( rowSet.getRowWait( anyLong(), any( TimeUnit.class ) ) ).thenAnswer( answer );
when( rowSet.getRow() ).thenAnswer( answer );
when( rowSet.isDone() ).thenAnswer( new Answer<Boolean>() {
@Override
public Boolean answer( InvocationOnMock invocation ) throws Throwable {
return index.get() >= rows.size();
}
} );
return rowSet;
}
public static List<Object[]> asList( Object[]... objects ) {
List<Object[]> result = new ArrayList<Object[]>();
Collections.addAll( result, objects );
return result;
}
public void cleanUp() {
KettleLogStore.setLogChannelInterfaceFactory( originalLogChannelInterfaceFactory );
}
/**
* In case you need to use log methods during the tests
* use redirectLog method after creating new StepMockHelper object.
* Examples:
* stepMockHelper.redirectLog( System.out, LogLevel.ROWLEVEL );
* stepMockHelper.redirectLog( new FileOutputStream("log.txt"), LogLevel.BASIC );
*/
public void redirectLog( final OutputStream out, LogLevel channelLogLevel ) {
final LogChannel log = spy( new LogChannel( this.getClass().getName(), true ) );
log.setLogLevel( channelLogLevel );
when( logChannelInterfaceFactory.create( any(), any( LoggingObjectInterface.class ) ) ).thenReturn( log );
doAnswer( new Answer<Object>() {
@Override
public Object answer( InvocationOnMock invocation ) throws Throwable {
Object[] args = invocation.getArguments();
LogLevel logLevel = (LogLevel) args[1];
LogLevel channelLogLevel = log.getLogLevel();
if ( !logLevel.isVisible( channelLogLevel ) ) {
return null; // not for our eyes.
}
if ( channelLogLevel.getLevel() >= logLevel.getLevel() ) {
LogMessageInterface logMessage = (LogMessageInterface) args[0];
out.write( logMessage.getMessage().getBytes() );
out.write( '\n' );
out.write( '\r' );
out.flush();
return true;
}
return false;
}
} ).when( log ).println( (LogMessageInterface) anyObject(), (LogLevel) anyObject() );
}
}