/*! ****************************************************************************** * * 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.univariatestats; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.IOException; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.pentaho.di.core.CheckResultInterface; import org.pentaho.di.core.database.DatabaseMeta; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.exception.KettleStepException; import org.pentaho.di.core.exception.KettleXMLException; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.row.value.ValueMetaBase; import org.pentaho.di.core.xml.XMLHandler; 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.StepInterface; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.di.trans.steps.loadsave.LoadSaveTester; import org.pentaho.di.trans.steps.loadsave.validator.ArrayLoadSaveValidator; import org.pentaho.di.trans.steps.loadsave.validator.FieldLoadSaveValidator; import org.pentaho.metastore.api.IMetaStore; public class UnivariateStatsMetaTest { private final FieldLoadSaveValidator<UnivariateStatsMetaFunction> univariateFunctionFieldLoadSaveValidator = new FieldLoadSaveValidator<UnivariateStatsMetaFunction>() { final Random random = new Random(); @Override public boolean validateTestObject( UnivariateStatsMetaFunction testObject, Object actual ) { return testObject.getXML().equals( ( (UnivariateStatsMetaFunction) actual ).getXML() ); } @Override public UnivariateStatsMetaFunction getTestObject() { return new UnivariateStatsMetaFunction( UUID.randomUUID().toString(), random.nextBoolean(), random .nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextDouble(), random.nextBoolean() ); } }; private final ArrayLoadSaveValidator<UnivariateStatsMetaFunction> univariateFunctionArrayFieldLoadSaveValidator = new ArrayLoadSaveValidator<UnivariateStatsMetaFunction>( univariateFunctionFieldLoadSaveValidator ); @Test public void testGetAndSetSetInputFieldMetaFunctions() { UnivariateStatsMetaFunction[] stats = new UnivariateStatsMetaFunction[3]; UnivariateStatsMeta meta = new UnivariateStatsMeta(); meta.setInputFieldMetaFunctions( stats ); assertTrue( stats == meta.getInputFieldMetaFunctions() ); } @Test public void testAllocateAndGetNumFieldsToProcess() { UnivariateStatsMeta meta = new UnivariateStatsMeta(); meta.allocate( 13 ); assertEquals( 13, meta.getNumFieldsToProcess() ); } @Test public void testLegacyLoadXml() throws IOException, KettleXMLException { String legacyXml = IOUtils.toString( UnivariateStatsMetaTest.class.getClassLoader().getResourceAsStream( "org/pentaho/di/trans/steps/univariatestats/legacyUnivariateStatsMetaTest.xml" ) ); IMetaStore mockMetaStore = mock( IMetaStore.class ); UnivariateStatsMeta meta = new UnivariateStatsMeta(); meta.loadXML( XMLHandler.loadXMLString( legacyXml ).getFirstChild(), new ArrayList<DatabaseMeta>(), mockMetaStore ); assertEquals( 2, meta.getNumFieldsToProcess() ); UnivariateStatsMetaFunction first = meta.getInputFieldMetaFunctions()[0]; assertEquals( "a", first.getSourceFieldName() ); assertEquals( true, first.getCalcN() ); assertEquals( true, first.getCalcMean() ); assertEquals( true, first.getCalcStdDev() ); assertEquals( true, first.getCalcMin() ); assertEquals( true, first.getCalcMax() ); assertEquals( true, first.getCalcMedian() ); assertEquals( .5, first.getCalcPercentile(), 0 ); assertEquals( true, first.getInterpolatePercentile() ); UnivariateStatsMetaFunction second = meta.getInputFieldMetaFunctions()[1]; assertEquals( "b", second.getSourceFieldName() ); assertEquals( false, second.getCalcN() ); assertEquals( false, second.getCalcMean() ); assertEquals( false, second.getCalcStdDev() ); assertEquals( false, second.getCalcMin() ); assertEquals( false, second.getCalcMax() ); assertEquals( false, second.getCalcMedian() ); assertEquals( -1.0, second.getCalcPercentile(), 0 ); assertEquals( false, second.getInterpolatePercentile() ); } @Test public void loadSaveRoundTripTest() throws KettleException { List<String> attributes = Arrays.asList( "inputFieldMetaFunctions" ); Map<String, FieldLoadSaveValidator<?>> fieldLoadSaveValidatorTypeMap = new HashMap<String, FieldLoadSaveValidator<?>>(); fieldLoadSaveValidatorTypeMap.put( UnivariateStatsMetaFunction[].class.getCanonicalName(), univariateFunctionArrayFieldLoadSaveValidator ); LoadSaveTester loadSaveTester = new LoadSaveTester( UnivariateStatsMeta.class, attributes, new HashMap<String, String>(), new HashMap<String, String>(), new HashMap<String, FieldLoadSaveValidator<?>>(), fieldLoadSaveValidatorTypeMap ); loadSaveTester.testSerialization(); } private void assertContains( Map<String, Integer> map, String key, Integer value ) { assertTrue( "Expected map to contain " + key, map.containsKey( key ) ); assertTrue( "Expected key of " + key + " to be of type " + ValueMetaBase.getTypeDesc( value ), map.get( key ) == value ); } @Test public void testGetFields() throws KettleStepException { UnivariateStatsMeta meta = new UnivariateStatsMeta(); UnivariateStatsMetaFunction[] functions = univariateFunctionArrayFieldLoadSaveValidator.getTestObject(); meta.setInputFieldMetaFunctions( functions ); RowMetaInterface mockRowMetaInterface = mock( RowMetaInterface.class ); final AtomicBoolean clearCalled = new AtomicBoolean( false ); final List<ValueMetaInterface> valueMetaInterfaces = new ArrayList<ValueMetaInterface>(); doAnswer( new Answer<Void>() { @Override public Void answer( InvocationOnMock invocation ) throws Throwable { clearCalled.set( true ); return null; } } ).when( mockRowMetaInterface ).clear(); doAnswer( new Answer<Void>() { @Override public Void answer( InvocationOnMock invocation ) throws Throwable { if ( !clearCalled.get() ) { throw new RuntimeException( "Clear not called before adding value metas" ); } valueMetaInterfaces.add( (ValueMetaInterface) invocation.getArguments()[0] ); return null; } } ).when( mockRowMetaInterface ).addValueMeta( any( ValueMetaInterface.class ) ); meta.getFields( mockRowMetaInterface, null, null, null, null, null, null ); Map<String, Integer> valueMetas = new HashMap<String, Integer>(); for ( ValueMetaInterface vmi : valueMetaInterfaces ) { valueMetas.put( vmi.getName(), vmi.getType() ); } for ( UnivariateStatsMetaFunction function : functions ) { if ( function.getCalcN() ) { assertContains( valueMetas, function.getSourceFieldName() + "(N)", ValueMetaInterface.TYPE_NUMBER ); } if ( function.getCalcMean() ) { assertContains( valueMetas, function.getSourceFieldName() + "(mean)", ValueMetaInterface.TYPE_NUMBER ); } if ( function.getCalcStdDev() ) { assertContains( valueMetas, function.getSourceFieldName() + "(stdDev)", ValueMetaInterface.TYPE_NUMBER ); } if ( function.getCalcMin() ) { assertContains( valueMetas, function.getSourceFieldName() + "(min)", ValueMetaInterface.TYPE_NUMBER ); } if ( function.getCalcMax() ) { assertContains( valueMetas, function.getSourceFieldName() + "(max)", ValueMetaInterface.TYPE_NUMBER ); } if ( function.getCalcMedian() ) { assertContains( valueMetas, function.getSourceFieldName() + "(median)", ValueMetaInterface.TYPE_NUMBER ); } if ( function.getCalcPercentile() >= 0 ) { NumberFormat pF = NumberFormat.getInstance(); pF.setMaximumFractionDigits( 2 ); String res = pF.format( function.getCalcPercentile() * 100 ); assertContains( valueMetas, function.getSourceFieldName() + "(" + res + "th percentile)", ValueMetaInterface.TYPE_NUMBER ); } } } @Test public void testCheckNullPrev() { UnivariateStatsMeta meta = new UnivariateStatsMeta(); List<CheckResultInterface> remarks = new ArrayList<CheckResultInterface>(); meta.check( remarks, null, null, null, new String[0], null, null, null, null, null ); assertEquals( 2, remarks.size() ); assertEquals( "Not receiving any fields from previous steps!", remarks.get( 0 ).getText() ); } @Test public void testCheckEmptyPrev() { UnivariateStatsMeta meta = new UnivariateStatsMeta(); RowMetaInterface mockRowMetaInterface = mock( RowMetaInterface.class ); when( mockRowMetaInterface.size() ).thenReturn( 0 ); List<CheckResultInterface> remarks = new ArrayList<CheckResultInterface>(); meta.check( remarks, null, null, mockRowMetaInterface, new String[0], null, null, null, null, null ); assertEquals( 2, remarks.size() ); assertEquals( "Not receiving any fields from previous steps!", remarks.get( 0 ).getText() ); } @Test public void testCheckGoodPrev() { UnivariateStatsMeta meta = new UnivariateStatsMeta(); RowMetaInterface mockRowMetaInterface = mock( RowMetaInterface.class ); when( mockRowMetaInterface.size() ).thenReturn( 500 ); List<CheckResultInterface> remarks = new ArrayList<CheckResultInterface>(); meta.check( remarks, null, null, mockRowMetaInterface, new String[0], null, null, null, null, null ); assertEquals( 2, remarks.size() ); assertEquals( "Step is connected to previous one, receiving " + 500 + " fields", remarks.get( 0 ).getText() ); } @Test public void testCheckWithInput() { UnivariateStatsMeta meta = new UnivariateStatsMeta(); List<CheckResultInterface> remarks = new ArrayList<CheckResultInterface>(); meta.check( remarks, null, null, null, new String[1], null, null, null, null, null ); assertEquals( 2, remarks.size() ); assertEquals( "Step is receiving info from other steps.", remarks.get( 1 ).getText() ); } @Test public void testCheckWithoutInput() { UnivariateStatsMeta meta = new UnivariateStatsMeta(); List<CheckResultInterface> remarks = new ArrayList<CheckResultInterface>(); meta.check( remarks, null, null, null, new String[0], null, null, null, null, null ); assertEquals( 2, remarks.size() ); assertEquals( "No input received from other steps!", remarks.get( 1 ).getText() ); } @Test public void testGetStep() { StepMeta mockStepMeta = mock( StepMeta.class ); when( mockStepMeta.getName() ).thenReturn( "testName" ); StepDataInterface mockStepDataInterface = mock( StepDataInterface.class ); int cnr = 10; TransMeta mockTransMeta = mock( TransMeta.class ); Trans mockTrans = mock( Trans.class ); when( mockTransMeta.findStep( "testName" ) ).thenReturn( mockStepMeta ); StepInterface step = new UnivariateStatsMeta().getStep( mockStepMeta, mockStepDataInterface, cnr, mockTransMeta, mockTrans ); assertTrue( "Expected Step to be instanceof " + UnivariateStats.class, step instanceof UnivariateStats ); } @Test public void testGetStepData() { assertTrue( "Expected StepData to be instanceof " + UnivariateStatsData.class, new UnivariateStatsMeta() .getStepData() instanceof UnivariateStatsData ); } }