/*! ******************************************************************************
*
* 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.calculator;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.KettleEnvironment;
import org.pentaho.di.core.RowSet;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleStepException;
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.ValueMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaDate;
import org.pentaho.di.core.row.value.ValueMetaBigNumber;
import org.pentaho.di.core.row.value.ValueMetaInteger;
import org.pentaho.di.core.row.value.ValueMetaNumber;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.trans.step.RowAdapter;
import org.pentaho.di.trans.steps.mock.StepMockHelper;
import junit.framework.Assert;
/**
* Unit tests for calculator step
*
* @author Pavel Sakun
* @see Calculator
*/
public class CalculatorUnitTest {
private StepMockHelper<CalculatorMeta, CalculatorData> smh;
@BeforeClass
public static void init() throws KettleException {
KettleEnvironment.init( false );
}
@Before
public void setUp() {
smh =
new StepMockHelper<CalculatorMeta, CalculatorData>( "Calculator", CalculatorMeta.class,
CalculatorData.class );
when( smh.logChannelInterfaceFactory.create( any(), any( LoggingObjectInterface.class ) ) ).thenReturn(
smh.logChannelInterface );
when( smh.trans.isRunning() ).thenReturn( true );
}
@Test
public void testAddSeconds() throws KettleException {
RowMeta inputRowMeta = new RowMeta();
ValueMetaDate dayMeta = new ValueMetaDate( "Day" );
inputRowMeta.addValueMeta( dayMeta );
ValueMetaInteger secondsMeta = new ValueMetaInteger( "Seconds" );
inputRowMeta.addValueMeta( secondsMeta );
RowSet inputRowSet = null;
try {
inputRowSet = smh.getMockInputRowSet( new Object[][] {
{ new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).parse( "2014-01-01 00:00:00" ), new Long( 10 ) },
{ new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).parse( "2014-10-31 23:59:50" ), new Long( 30 ) } } );
} catch ( ParseException pe ) {
pe.printStackTrace();
fail();
}
inputRowSet.setRowMeta( inputRowMeta );
Calculator calculator = new Calculator( smh.stepMeta, smh.stepDataInterface, 0, smh.transMeta, smh.trans );
calculator.getInputRowSets().add( inputRowSet );
calculator.setInputRowMeta( inputRowMeta );
calculator.init( smh.initStepMetaInterface, smh.initStepDataInterface );
CalculatorMeta meta = new CalculatorMeta();
meta.setCalculation( new CalculatorMetaFunction[] {
new CalculatorMetaFunction( "new_day", CalculatorMetaFunction.CALC_ADD_SECONDS, "Day", "Seconds", null,
ValueMetaInterface.TYPE_DATE, 0, 0, false, "", "", "", "" ) } );
//Verify output
try {
calculator.addRowListener( new RowAdapter() {
@Override public void rowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException {
try {
assertEquals( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).parse( "2014-01-01 00:00:10" ), row[ 2 ] );
} catch ( ParseException pe ) {
throw new KettleStepException( pe );
}
}
} );
calculator.processRow( meta, new CalculatorData() );
} catch ( KettleException ke ) {
ke.printStackTrace();
fail();
}
}
@Test
public void testReturnDigitsOnly() throws KettleException {
RowMeta inputRowMeta = new RowMeta();
ValueMetaString nameMeta = new ValueMetaString( "Name" );
inputRowMeta.addValueMeta( nameMeta );
ValueMetaString valueMeta = new ValueMetaString( "Value" );
inputRowMeta.addValueMeta( valueMeta );
RowSet inputRowSet = smh.getMockInputRowSet( new Object[][] { { "name1", "qwe123asd456zxc" }, { "name2", null } } );
inputRowSet.setRowMeta( inputRowMeta );
Calculator calculator = new Calculator( smh.stepMeta, smh.stepDataInterface, 0, smh.transMeta, smh.trans );
calculator.getInputRowSets().add( inputRowSet );
calculator.setInputRowMeta( inputRowMeta );
calculator.init( smh.initStepMetaInterface, smh.initStepDataInterface );
CalculatorMeta meta = new CalculatorMeta();
meta.setCalculation( new CalculatorMetaFunction[] {
new CalculatorMetaFunction( "digits", CalculatorMetaFunction.CALC_GET_ONLY_DIGITS, "Value", null, null,
ValueMetaInterface.TYPE_STRING, 0, 0, false, "", "", "", "" ) } );
// Verify output
try {
calculator.addRowListener( new RowAdapter() {
@Override public void rowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException {
assertEquals( "123456", row[ 2 ] );
}
} );
calculator.processRow( meta, new CalculatorData() );
} catch ( KettleException ke ) {
ke.printStackTrace();
fail();
}
}
@Test
public void calculatorShouldClearDataInstance() throws Exception {
RowMeta inputRowMeta = new RowMeta();
ValueMetaInteger valueMeta = new ValueMetaInteger( "Value" );
inputRowMeta.addValueMeta( valueMeta );
RowSet inputRowSet = smh.getMockInputRowSet( new Object[] { -1L } );
inputRowSet.setRowMeta( inputRowMeta );
Calculator calculator = new Calculator( smh.stepMeta, smh.stepDataInterface, 0, smh.transMeta, smh.trans );
calculator.getInputRowSets().add( inputRowSet );
calculator.setInputRowMeta( inputRowMeta );
calculator.init( smh.initStepMetaInterface, smh.initStepDataInterface );
CalculatorMeta meta = new CalculatorMeta();
meta.setCalculation( new CalculatorMetaFunction[] {
new CalculatorMetaFunction( "test", CalculatorMetaFunction.CALC_ABS, "Value", null, null,
ValueMetaInterface.TYPE_STRING, 0, 0, false, "", "", "", "" ) } );
CalculatorData data = new CalculatorData();
data = spy( data );
calculator.processRow( meta, data );
verify( data ).getValueMetaFor( eq( valueMeta.getType() ), anyString() );
calculator.processRow( meta, data );
verify( data ).clearValuesMetaMapping();
}
@Test
public void testRound1() throws KettleException {
assertRound1( 1.0, 1.2 );
assertRound1( 2.0, 1.5 );
assertRound1( 2.0, 1.7 );
assertRound1( 2.0, 2.2 );
assertRound1( 3.0, 2.5 );
assertRound1( 3.0, 2.7 );
assertRound1( -1.0, -1.2 );
assertRound1( -1.0, -1.5 );
assertRound1( -2.0, -1.7 );
assertRound1( -2.0, -2.2 );
assertRound1( -2.0, -2.5 );
assertRound1( -3.0, -2.7 );
assertRound1( 1.0, 1.0 );
assertRound1( 2.0, 2.0 );
assertRound1( -3.0, -3.0 );
}
@Test
public void testRound2() throws KettleException {
assertRound2( 1.0, 1.2, 0 );
assertRound2( 2.0, 1.5, 0 );
assertRound2( 2.0, 1.7, 0 );
assertRound2( 2.0, 2.2, 0 );
assertRound2( 3.0, 2.5, 0 );
assertRound2( 3.0, 2.7, 0 );
assertRound2( -1.0, -1.2, 0 );
assertRound2( -1.0, -1.5, 0 );
assertRound2( -2.0, -1.7, 0 );
assertRound2( -2.0, -2.2, 0 );
assertRound2( -2.0, -2.5, 0 );
assertRound2( -3.0, -2.7, 0 );
assertRound2( 1.0, 1.0, 0 );
assertRound2( 2.0, 2.0, 0 );
assertRound2( -3.0, -3.0, 0 );
assertRound2( 0.010, 0.012, 2 );
assertRound2( 0.020, 0.015, 2 );
assertRound2( 0.020, 0.017, 2 );
assertRound2( 0.020, 0.022, 2 );
assertRound2( 0.030, 0.025, 2 );
assertRound2( 0.030, 0.027, 2 );
assertRound2( -0.010, -0.012, 2 );
assertRound2( -0.010, -0.015, 2 );
assertRound2( -0.020, -0.017, 2 );
assertRound2( -0.020, -0.022, 2 );
assertRound2( -0.020, -0.025, 2 );
assertRound2( -0.030, -0.027, 2 );
assertRound2( 0.010, 0.010, 2 );
assertRound2( 0.020, 0.020, 2 );
assertRound2( -0.030, -0.030, 2 );
assertRound2( 100, 120, -2 );
assertRound2( 200, 150, -2 );
assertRound2( 200, 170, -2 );
assertRound2( 200, 220, -2 );
assertRound2( 300, 250, -2 );
assertRound2( 300, 270, -2 );
assertRound2( -100, -120, -2 );
assertRound2( -100, -150, -2 );
assertRound2( -200, -170, -2 );
assertRound2( -200, -220, -2 );
assertRound2( -200, -250, -2 );
assertRound2( -300, -270, -2 );
assertRound2( 100, 100, -2 );
assertRound2( 200, 200, -2 );
assertRound2( -300, -300, -2 );
}
@Test
public void testRoundStd1() throws KettleException {
assertRoundStd1( 1.0, 1.2 );
assertRoundStd1( 2.0, 1.5 );
assertRoundStd1( 2.0, 1.7 );
assertRoundStd1( 2.0, 2.2 );
assertRoundStd1( 3.0, 2.5 );
assertRoundStd1( 3.0, 2.7 );
assertRoundStd1( -1.0, -1.2 );
assertRoundStd1( -2.0, -1.5 );
assertRoundStd1( -2.0, -1.7 );
assertRoundStd1( -2.0, -2.2 );
assertRoundStd1( -3.0, -2.5 );
assertRoundStd1( -3.0, -2.7 );
assertRoundStd1( 1.0, 1.0 );
assertRoundStd1( 2.0, 2.0 );
assertRoundStd1( -3.0, -3.0 );
}
@Test
public void testRoundStd2() throws KettleException {
assertRoundStd2( 1.0, 1.2, 0 );
assertRoundStd2( 2.0, 1.5, 0 );
assertRoundStd2( 2.0, 1.7, 0 );
assertRoundStd2( 2.0, 2.2, 0 );
assertRoundStd2( 3.0, 2.5, 0 );
assertRoundStd2( 3.0, 2.7, 0 );
assertRoundStd2( -1.0, -1.2, 0 );
assertRoundStd2( -2.0, -1.5, 0 );
assertRoundStd2( -2.0, -1.7, 0 );
assertRoundStd2( -2.0, -2.2, 0 );
assertRoundStd2( -3.0, -2.5, 0 );
assertRoundStd2( -3.0, -2.7, 0 );
}
@Test
public void testRoundCustom1() throws KettleException {
assertRoundCustom1( 2.0, 1.2, BigDecimal.ROUND_UP );
assertRoundCustom1( 1.0, 1.2, BigDecimal.ROUND_DOWN );
assertRoundCustom1( 2.0, 1.2, BigDecimal.ROUND_CEILING );
assertRoundCustom1( 1.0, 1.2, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( 1.0, 1.2, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( 1.0, 1.2, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( 1.0, 1.2, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( 1.0, 1.2, Const.ROUND_HALF_CEILING );
assertRoundCustom1( 2.0, 1.5, BigDecimal.ROUND_UP );
assertRoundCustom1( 1.0, 1.5, BigDecimal.ROUND_DOWN );
assertRoundCustom1( 2.0, 1.5, BigDecimal.ROUND_CEILING );
assertRoundCustom1( 1.0, 1.5, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( 2.0, 1.5, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( 1.0, 1.5, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( 2.0, 1.5, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( 2.0, 1.5, Const.ROUND_HALF_CEILING );
assertRoundCustom1( 2.0, 1.7, BigDecimal.ROUND_UP );
assertRoundCustom1( 1.0, 1.7, BigDecimal.ROUND_DOWN );
assertRoundCustom1( 2.0, 1.7, BigDecimal.ROUND_CEILING );
assertRoundCustom1( 1.0, 1.7, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( 2.0, 1.7, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( 2.0, 1.7, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( 2.0, 1.7, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( 2.0, 1.7, Const.ROUND_HALF_CEILING );
assertRoundCustom1( 3.0, 2.2, BigDecimal.ROUND_UP );
assertRoundCustom1( 2.0, 2.2, BigDecimal.ROUND_DOWN );
assertRoundCustom1( 3.0, 2.2, BigDecimal.ROUND_CEILING );
assertRoundCustom1( 2.0, 2.2, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( 2.0, 2.2, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( 2.0, 2.2, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( 2.0, 2.2, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( 2.0, 2.2, Const.ROUND_HALF_CEILING );
assertRoundCustom1( 3.0, 2.5, BigDecimal.ROUND_UP );
assertRoundCustom1( 2.0, 2.5, BigDecimal.ROUND_DOWN );
assertRoundCustom1( 3.0, 2.5, BigDecimal.ROUND_CEILING );
assertRoundCustom1( 2.0, 2.5, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( 3.0, 2.5, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( 2.0, 2.5, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( 2.0, 2.5, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( 3.0, 2.5, Const.ROUND_HALF_CEILING );
assertRoundCustom1( 3.0, 2.7, BigDecimal.ROUND_UP );
assertRoundCustom1( 2.0, 2.7, BigDecimal.ROUND_DOWN );
assertRoundCustom1( 3.0, 2.7, BigDecimal.ROUND_CEILING );
assertRoundCustom1( 2.0, 2.7, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( 3.0, 2.7, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( 3.0, 2.7, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( 3.0, 2.7, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( 3.0, 2.7, Const.ROUND_HALF_CEILING );
assertRoundCustom1( -2.0, -1.2, BigDecimal.ROUND_UP );
assertRoundCustom1( -1.0, -1.2, BigDecimal.ROUND_DOWN );
assertRoundCustom1( -1.0, -1.2, BigDecimal.ROUND_CEILING );
assertRoundCustom1( -2.0, -1.2, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( -1.0, -1.2, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( -1.0, -1.2, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( -1.0, -1.2, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( -1.0, -1.2, Const.ROUND_HALF_CEILING );
assertRoundCustom1( -2.0, -1.5, BigDecimal.ROUND_UP );
assertRoundCustom1( -1.0, -1.5, BigDecimal.ROUND_DOWN );
assertRoundCustom1( -1.0, -1.5, BigDecimal.ROUND_CEILING );
assertRoundCustom1( -2.0, -1.5, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( -2.0, -1.5, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( -1.0, -1.5, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( -2.0, -1.5, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( -1.0, -1.5, Const.ROUND_HALF_CEILING );
assertRoundCustom1( -2.0, -1.7, BigDecimal.ROUND_UP );
assertRoundCustom1( -1.0, -1.7, BigDecimal.ROUND_DOWN );
assertRoundCustom1( -1.0, -1.7, BigDecimal.ROUND_CEILING );
assertRoundCustom1( -2.0, -1.7, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( -2.0, -1.7, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( -2.0, -1.7, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( -2.0, -1.7, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( -2.0, -1.7, Const.ROUND_HALF_CEILING );
assertRoundCustom1( -3.0, -2.2, BigDecimal.ROUND_UP );
assertRoundCustom1( -2.0, -2.2, BigDecimal.ROUND_DOWN );
assertRoundCustom1( -2.0, -2.2, BigDecimal.ROUND_CEILING );
assertRoundCustom1( -3.0, -2.2, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( -2.0, -2.2, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( -2.0, -2.2, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( -2.0, -2.2, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( -2.0, -2.2, Const.ROUND_HALF_CEILING );
assertRoundCustom1( -3.0, -2.5, BigDecimal.ROUND_UP );
assertRoundCustom1( -2.0, -2.5, BigDecimal.ROUND_DOWN );
assertRoundCustom1( -2.0, -2.5, BigDecimal.ROUND_CEILING );
assertRoundCustom1( -3.0, -2.5, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( -3.0, -2.5, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( -2.0, -2.5, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( -2.0, -2.5, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( -2.0, -2.5, Const.ROUND_HALF_CEILING );
assertRoundCustom1( -3.0, -2.7, BigDecimal.ROUND_UP );
assertRoundCustom1( -2.0, -2.7, BigDecimal.ROUND_DOWN );
assertRoundCustom1( -2.0, -2.7, BigDecimal.ROUND_CEILING );
assertRoundCustom1( -3.0, -2.7, BigDecimal.ROUND_FLOOR );
assertRoundCustom1( -3.0, -2.7, BigDecimal.ROUND_HALF_UP );
assertRoundCustom1( -3.0, -2.7, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom1( -3.0, -2.7, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom1( -3.0, -2.7, Const.ROUND_HALF_CEILING );
}
@Test
public void testRoundCustom2() throws KettleException {
assertRoundCustom2( 2.0, 1.2, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( 1.0, 1.2, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( 2.0, 1.2, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( 1.0, 1.2, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( 1.0, 1.2, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( 1.0, 1.2, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( 1.0, 1.2, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( 1.0, 1.2, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( 2.0, 1.5, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( 1.0, 1.5, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( 2.0, 1.5, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( 1.0, 1.5, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( 2.0, 1.5, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( 1.0, 1.5, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( 2.0, 1.5, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( 2.0, 1.5, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( 2.0, 1.7, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( 1.0, 1.7, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( 2.0, 1.7, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( 1.0, 1.7, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( 2.0, 1.7, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( 2.0, 1.7, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( 2.0, 1.7, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( 2.0, 1.7, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( 3.0, 2.2, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( 2.0, 2.2, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( 3.0, 2.2, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( 2.0, 2.2, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( 2.0, 2.2, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( 2.0, 2.2, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( 2.0, 2.2, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( 2.0, 2.2, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( 3.0, 2.5, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( 2.0, 2.5, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( 3.0, 2.5, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( 2.0, 2.5, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( 3.0, 2.5, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( 2.0, 2.5, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( 2.0, 2.5, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( 3.0, 2.5, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( 3.0, 2.7, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( 2.0, 2.7, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( 3.0, 2.7, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( 2.0, 2.7, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( 3.0, 2.7, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( 3.0, 2.7, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( 3.0, 2.7, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( 3.0, 2.7, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( -2.0, -1.2, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( -1.0, -1.2, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( -1.0, -1.2, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( -2.0, -1.2, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( -1.0, -1.2, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( -1.0, -1.2, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( -1.0, -1.2, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( -1.0, -1.2, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( -2.0, -1.5, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( -1.0, -1.5, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( -1.0, -1.5, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( -2.0, -1.5, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( -2.0, -1.5, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( -1.0, -1.5, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( -2.0, -1.5, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( -1.0, -1.5, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( -2.0, -1.7, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( -1.0, -1.7, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( -1.0, -1.7, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( -2.0, -1.7, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( -2.0, -1.7, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( -2.0, -1.7, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( -2.0, -1.7, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( -2.0, -1.7, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( -3.0, -2.2, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( -2.0, -2.2, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( -2.0, -2.2, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( -3.0, -2.2, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( -2.0, -2.2, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( -2.0, -2.2, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( -2.0, -2.2, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( -2.0, -2.2, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( -3.0, -2.5, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( -2.0, -2.5, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( -2.0, -2.5, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( -3.0, -2.5, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( -3.0, -2.5, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( -2.0, -2.5, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( -2.0, -2.5, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( -2.0, -2.5, 0, Const.ROUND_HALF_CEILING );
assertRoundCustom2( -3.0, -2.7, 0, BigDecimal.ROUND_UP );
assertRoundCustom2( -2.0, -2.7, 0, BigDecimal.ROUND_DOWN );
assertRoundCustom2( -2.0, -2.7, 0, BigDecimal.ROUND_CEILING );
assertRoundCustom2( -3.0, -2.7, 0, BigDecimal.ROUND_FLOOR );
assertRoundCustom2( -3.0, -2.7, 0, BigDecimal.ROUND_HALF_UP );
assertRoundCustom2( -3.0, -2.7, 0, BigDecimal.ROUND_HALF_DOWN );
assertRoundCustom2( -3.0, -2.7, 0, BigDecimal.ROUND_HALF_EVEN );
assertRoundCustom2( -3.0, -2.7, 0, Const.ROUND_HALF_CEILING );
}
public void assertRoundGeneral( final Object expectedResult, final int calcFunction, final Number value,
final Long precision, final Long roundingMode, final int valueDataType, final int functionDataType ) throws KettleException {
final String msg = getKettleTypeName( valueDataType ) + "->" + getKettleTypeName( functionDataType ) + " ";
final RowMeta inputRowMeta = new RowMeta();
final List<Object> inputValues = new ArrayList<Object>( 3 );
final String fieldValue = "Value";
final ValueMetaInterface valueMeta;
switch ( valueDataType ) {
case ValueMetaInterface.TYPE_BIGNUMBER:
valueMeta = new ValueMetaBigNumber( fieldValue );
break;
case ValueMetaInterface.TYPE_NUMBER:
valueMeta = new ValueMetaNumber( fieldValue );
break;
case ValueMetaInterface.TYPE_INTEGER:
valueMeta = new ValueMetaInteger( fieldValue );
break;
default:
throw new IllegalArgumentException( msg + "Unexpected value dataType: " + value.getClass().getName()
+ ". Long, Double or BigDecimal expected." );
}
inputRowMeta.addValueMeta( valueMeta );
inputValues.add( value );
final String fieldPrecision;
final ValueMetaInteger precisionMeta;
if ( precision == null ) {
fieldPrecision = null;
precisionMeta = null;
} else {
fieldPrecision = "Precision";
precisionMeta = new ValueMetaInteger( fieldPrecision );
inputRowMeta.addValueMeta( precisionMeta );
inputValues.add( precision );
}
final String fieldRoundingMode;
final ValueMetaInteger roundingModeMeta;
if ( roundingMode == null ) {
fieldRoundingMode = null;
roundingModeMeta = null;
} else {
fieldRoundingMode = "RoundingMode";
roundingModeMeta = new ValueMetaInteger( fieldRoundingMode );
inputRowMeta.addValueMeta( roundingModeMeta );
inputValues.add( roundingMode );
}
RowSet inputRowSet = smh.getMockInputRowSet( inputValues.toArray() );
inputRowSet.setRowMeta( inputRowMeta );
final String fieldA = inputRowMeta.size() > 0 ? inputRowMeta.getValueMetaList().get( 0 ).getName() : null;
final String fieldB = inputRowMeta.size() > 1 ? inputRowMeta.getValueMetaList().get( 1 ).getName() : null;
final String fieldC = inputRowMeta.size() > 2 ? inputRowMeta.getValueMetaList().get( 2 ).getName() : null;
final int resultDataType = functionDataType;
final String fieldResult = "test";
final int expectedResultRowSize = inputRowMeta.size() + 1;
Calculator calculator = new Calculator( smh.stepMeta, smh.stepDataInterface, 0, smh.transMeta, smh.trans );
calculator.getInputRowSets().add( inputRowSet );
calculator.setInputRowMeta( inputRowMeta );
calculator.init( smh.initStepMetaInterface, smh.initStepDataInterface );
CalculatorMeta meta = new CalculatorMeta();
meta.setCalculation( new CalculatorMetaFunction[] { new CalculatorMetaFunction( fieldResult, calcFunction, fieldA,
fieldB, fieldC, resultDataType, 2, 0, false, "", "", "", "" ) } );
// Verify output
try {
calculator.addRowListener( new RowAdapter() {
@Override
public void rowWrittenEvent( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException {
assertEquals( msg + " resultRowSize", expectedResultRowSize, rowMeta.size() );
final int fieldResultIndex = rowMeta.size() - 1;
assertEquals( msg + " fieldResult", fieldResult, rowMeta.getValueMeta( fieldResultIndex ).getName() );
assertEquals( msg, expectedResult, row[fieldResultIndex] );
}
} );
calculator.processRow( meta, new CalculatorData() );
} catch ( KettleException ke ) {
ke.printStackTrace();
fail( msg + ke.getMessage() );
}
}
/**
* Asserts different data types according to specified expectedResult and value.<br/>
* Double - TYPE_NUMBER, TYPE_BIGNUMBER<br/>
* Integer - TYPE_NUMBER, TYPE_BIGNUMBER, TYPE_INTEGER
*
* @param expectedResult
* Double and Integer values allowed
* @param calcFunction
* @param value
* @param precision
* @param roundingMode
* @throws KettleException
*/
public void assertRoundEveryDataType( final Number expectedResult, final int calcFunction, final Number value,
final Long precision, final Long roundingMode ) throws KettleException {
{
final double resultValue = expectedResult.doubleValue();
assertRoundGeneral( resultValue, calcFunction, value.doubleValue(), precision, roundingMode, ValueMetaInterface.TYPE_NUMBER,
ValueMetaInterface.TYPE_NUMBER );
assertRoundGeneral( resultValue, calcFunction, new BigDecimal( String.valueOf( value.doubleValue() ) ),
precision, roundingMode, ValueMetaInterface.TYPE_BIGNUMBER, ValueMetaInterface.TYPE_NUMBER );
if ( isInt( value ) ) {
assertRoundGeneral( resultValue, calcFunction, value.longValue(), precision, roundingMode, ValueMetaInterface.TYPE_INTEGER,
ValueMetaInterface.TYPE_NUMBER );
}
}
{
final BigDecimal resultValue = BigDecimal.valueOf( expectedResult.doubleValue() );
assertRoundGeneral( resultValue, calcFunction, value.doubleValue(), precision, roundingMode, ValueMetaInterface.TYPE_NUMBER,
ValueMetaInterface.TYPE_BIGNUMBER );
assertRoundGeneral( resultValue, calcFunction, new BigDecimal( String.valueOf( value.doubleValue() ) ),
precision, roundingMode, ValueMetaInterface.TYPE_BIGNUMBER, ValueMetaInterface.TYPE_BIGNUMBER );
if ( isInt( value ) ) {
assertRoundGeneral( resultValue, calcFunction, value.longValue(), precision, roundingMode, ValueMetaInterface.TYPE_INTEGER,
ValueMetaInterface.TYPE_BIGNUMBER );
}
}
if ( isInt( expectedResult ) ) {
final Long resultValue = expectedResult.longValue();
assertRoundGeneral( resultValue, calcFunction, value.doubleValue(), precision, roundingMode, ValueMetaInterface.TYPE_NUMBER,
ValueMetaInterface.TYPE_INTEGER );
assertRoundGeneral( resultValue, calcFunction, new BigDecimal( String.valueOf( value.doubleValue() ) ),
precision, roundingMode, ValueMetaInterface.TYPE_BIGNUMBER, ValueMetaInterface.TYPE_INTEGER );
if ( isInt( value ) ) {
assertRoundGeneral( resultValue, calcFunction, value.longValue(), precision, roundingMode, ValueMetaInterface.TYPE_INTEGER,
ValueMetaInterface.TYPE_INTEGER );
}
}
}
public void assertRound1( final Number expectedResult, final Number value ) throws KettleException {
assertRoundEveryDataType( expectedResult, CalculatorMetaFunction.CALC_ROUND_1, value, null, null );
}
public void assertRound2( final Number expectedResult, final Number value, final long precision )
throws KettleException {
assertRoundEveryDataType( expectedResult, CalculatorMetaFunction.CALC_ROUND_2, value, precision, null );
}
public void assertRoundStd1( final Number expectedResult, final Number value ) throws KettleException {
assertRoundEveryDataType( expectedResult, CalculatorMetaFunction.CALC_ROUND_STD_1, value, null, null );
}
public void assertRoundStd2( final Number expectedResult, final Number value, final long precision )
throws KettleException {
assertRoundEveryDataType( expectedResult, CalculatorMetaFunction.CALC_ROUND_STD_2, value, precision, null );
}
public void assertRoundCustom1( final Number expectedResult, final Number value, final long roundingMode )
throws KettleException {
assertRoundEveryDataType( expectedResult, CalculatorMetaFunction.CALC_ROUND_CUSTOM_1, value, null, roundingMode );
}
public void assertRoundCustom2( final Number expectedResult, final Number value, final long precision,
final long roundingMode ) throws KettleException {
assertRoundEveryDataType( expectedResult, CalculatorMetaFunction.CALC_ROUND_CUSTOM_2, value, precision, roundingMode );
}
/**
* Check whether value represents a whole number
*
* @param value
* @return
*/
private static boolean isInt( Number value ) {
if ( value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte
|| value instanceof BigInteger ) {
return true;
}
final BigDecimal bigDecimalValue;
if ( value instanceof Double || value instanceof Float ) {
bigDecimalValue = new BigDecimal( value.toString() );
} else if ( value instanceof BigDecimal ) {
bigDecimalValue = (BigDecimal) value;
} else {
throw new IllegalArgumentException( "Unexpected dataType: " + value.getClass().getName() );
}
try {
bigDecimalValue.longValueExact();
return true;
} catch ( ArithmeticException e ) {
return false;
}
}
private String getKettleTypeName( int kettleNumberDataType ) {
final String kettleNumberDataTypeName;
switch ( kettleNumberDataType ) {
case ValueMetaInterface.TYPE_BIGNUMBER:
kettleNumberDataTypeName = "BigNumber(" + kettleNumberDataType + ")";
break;
case ValueMetaInterface.TYPE_NUMBER:
kettleNumberDataTypeName = "Number(" + kettleNumberDataType + ")";
break;
case ValueMetaInterface.TYPE_INTEGER:
kettleNumberDataTypeName = "Integer(" + kettleNumberDataType + ")";
break;
default:
kettleNumberDataTypeName = "?(" + kettleNumberDataType + ")";
}
return kettleNumberDataTypeName;
}
public static void assertEquals( Object expected, Object actual ) {
assertEquals( null, expected, actual );
}
public static void assertEquals( String msg, Object expected, Object actual ) {
if ( expected instanceof BigDecimal && actual instanceof BigDecimal ) {
if ( ( (BigDecimal) expected ).compareTo( (BigDecimal) actual ) != 0 ) {
Assert.assertEquals( msg, expected, actual );
}
} else {
Assert.assertEquals( msg, expected, actual );
}
}
}