/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 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.jobexecutor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.logging.LoggingObjectInterface;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.job.Job;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.trans.steps.StepMockUtil;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
/**
* @author Mikhail_Chen-Len-Son
*/
public class JobExecutorTest {
private JobExecutor executor;
private JobExecutorMeta meta;
private JobExecutorData data;
@Before
public void setUp() throws Exception {
executor = StepMockUtil.getStep( JobExecutor.class, JobExecutorMeta.class, "TransExecutorUnitTest" );
executor = spy( executor );
executor.setInputRowMeta( mock( RowMetaInterface.class ) );
doNothing().when( executor ).discardLogLines( any( JobExecutorData.class ) );
meta = new JobExecutorMeta();
data = new JobExecutorData();
doReturn( mock( Job.class ) ).when( executor ).createJob( any( Repository.class ), any( JobMeta.class ),
any( LoggingObjectInterface.class ) );
data.groupBuffer = new ArrayList<>();
data.groupSize = -1;
data.groupTime = -1;
data.groupField = null;
}
@After
public void tearDown() {
executor = null;
meta = null;
data = null;
}
/**
* Given an input data and a job executor with specified field to group rows on.
* <br/>
* When job executor is processing rows of an input data,
* then rows should be accumulated in a group as long as the specified field value stays the same.
*/
@Test
public void shouldAccumulateRowsWhenGroupFieldIsSpecified() throws KettleException {
prepareMultipleRowsForExecutor();
data.groupField = "groupField";
executor.init( meta, data );
RowMetaInterface rowMeta = new RowMeta();
rowMeta.addValueMeta( new ValueMetaString( "groupField" ) );
executor.setInputRowMeta( rowMeta );
// start processing
executor.processRow( meta, data ); // 1st row - 'value1'
// should be added to group buffer
assertEquals( 1, data.groupBuffer.size() );
executor.processRow( meta, data );
executor.processRow( meta, data );
executor.processRow( meta, data ); // 4th row - still 'value1'
// first 4 rows should be added to the same group
assertEquals( 4, data.groupBuffer.size() );
executor.processRow( meta, data ); // 5th row - value has been changed - 'value12'
// previous group buffer should be flushed
// and a new group should be started
assertEquals( 1, data.groupBuffer.size() );
executor.processRow( meta, data ); // 6th row - 'value12'
executor.processRow( meta, data ); // 7th row - 'value12'
// the rest rows should be added to another group
assertEquals( 3, data.groupBuffer.size() );
executor.processRow( meta, data ); // end of file
// group buffer should be flushed in the end
assertEquals( 0, data.groupBuffer.size() );
}
/**
* Given an input data and a job executor
* with specified number of rows to send to the transformation (X).
* <br/>
* When job executor is processing rows of an input data,
* then every X rows should be accumulated in a group.
*/
@Test
public void shouldAccumulateRowsByCount() throws KettleException {
prepareMultipleRowsForExecutor();
data.groupSize = 5;
executor.init( meta, data );
// start processing
executor.processRow( meta, data ); // 1st row
// should be added to group buffer
assertEquals( 1, data.groupBuffer.size() );
executor.processRow( meta, data );
executor.processRow( meta, data );
executor.processRow( meta, data ); // 4th row
// first 4 rows should be added to the same group
assertEquals( 4, data.groupBuffer.size() );
executor.processRow( meta, data ); // 5th row
// once the 5th row is processed, the transformation executor should be triggered
// and thus, group buffer should be flushed
assertEquals( 0, data.groupBuffer.size() );
executor.processRow( meta, data ); // 6th row
// previous group buffer should be flushed
// and a new group should be started
assertEquals( 1, data.groupBuffer.size() );
executor.processRow( meta, data ); // 7th row
// the rest rows should be added to another group
assertEquals( 2, data.groupBuffer.size() );
executor.processRow( meta, data ); // end of file
// group buffer should be flushed in the end
assertEquals( 0, data.groupBuffer.size() );
}
// values to be grouped
private void prepareMultipleRowsForExecutor() throws KettleException {
doReturn( new Object[] { "value1" } )
.doReturn( new Object[] { "value1" } )
.doReturn( new Object[] { "value1" } )
.doReturn( new Object[] { "value1" } )
.doReturn( new Object[] { "value12" } )
.doReturn( new Object[] { "value12" } )
.doReturn( new Object[] { "value12" } )
.doReturn( null )
.when( executor ).getRow();
}
}