/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2013 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.mapping; import java.util.ArrayList; import java.util.Collections; import java.util.List; import junit.framework.TestCase; import org.pentaho.di.core.KettleEnvironment; import org.pentaho.di.core.ObjectLocationSpecificationMethod; import org.pentaho.di.core.plugins.PluginRegistry; import org.pentaho.di.core.plugins.StepPluginType; import org.pentaho.di.trans.RowStepCollector; import org.pentaho.di.trans.Trans; import org.pentaho.di.trans.TransHopMeta; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.StepIOMetaInterface; import org.pentaho.di.trans.step.StepInterface; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.di.trans.step.StepMetaInterface; import org.pentaho.di.trans.steps.rowgenerator.RowGeneratorMeta; public class MappingTest extends TestCase { /** * Builds a {@link org.pentaho.di.trans.steps.rowgenerator.RowGenerator} Step * with a single String field. * * @param registry * Plugin Registry. * @param stepName * Name to use for step * @return {@link StepMeta} for a Row Generator step. */ private StepMeta buildRowGeneratorStep( PluginRegistry registry, String stepName ) { RowGeneratorMeta rm = new RowGeneratorMeta(); // Set the information of the row generator. String rowGeneratorPid = registry.getPluginId( StepPluginType.class, rm ); StepMeta rowGeneratorStep = new StepMeta( rowGeneratorPid, stepName, rm ); String[] fieldName = { "string" }; String[] type = { "String" }; String[] value = { "string_value" }; String[] fieldFormat = { "" }; String[] group = { "" }; String[] decimal = { "" }; String[] currency = { "", }; int[] intDummies = { -1, -1, -1 }; boolean[] setEmptystring = { false, false, false }; rm.setDefault(); rm.setFieldName( fieldName ); rm.setFieldType( type ); rm.setValue( value ); rm.setFieldLength( intDummies ); rm.setFieldPrecision( intDummies ); rm.setRowLimit( "1" ); rm.setFieldFormat( fieldFormat ); rm.setGroup( group ); rm.setDecimal( decimal ); rm.setCurrency( currency ); rm.setEmptyString( setEmptystring ); return rowGeneratorStep; } private MappingIODefinition createMappingDef( String inputStepName, String mappingStepName, String sourceValueName, String targetValueName ) { MappingIODefinition def = new MappingIODefinition(); def.setInputStepname( inputStepName ); def.setOutputStepname( mappingStepName ); def.setValueRenames( Collections.singletonList( new MappingValueRename( sourceValueName, targetValueName ) ) ); return def; } /** * Tests that info steps are correctly identified via StepMetaInterface.getStepIOMeta() */ public void testInfoStreams_single() throws Exception { KettleEnvironment.init(); PluginRegistry registry = PluginRegistry.getInstance(); // // Create a new transformation with a row generator that feeds a Mapping (Sub-Transformation) Step // TransMeta transMeta = new TransMeta(); transMeta.setName( "Mapping Info Test" ); StepMeta rowGenerator = buildRowGeneratorStep( registry, "Generate Rows" ); transMeta.addStep( rowGenerator ); String mappingName = "mapping"; MappingMeta mappingMeta = new MappingMeta(); mappingMeta.setSpecificationMethod( ObjectLocationSpecificationMethod.FILENAME ); mappingMeta.setFileName( "test/org/pentaho/di/trans/steps/mapping/subtrans.ktr" ); String mappingInputStepName = "input"; mappingMeta.setInputMappings( Collections.singletonList( createMappingDef( rowGenerator.getName(), mappingInputStepName, "string", "a" ) ) ); String mappingPid = registry.getPluginId( StepPluginType.class, mappingMeta ); StepMeta mapping = new StepMeta( mappingPid, mappingName, mappingMeta ); transMeta.addStep( mapping ); TransHopMeta hopGeneratorToMapping = new TransHopMeta( rowGenerator, mapping ); transMeta.addTransHop( hopGeneratorToMapping ); Trans trans = new Trans( transMeta ); trans.prepareExecution( null ); // Mimic how a transformation is loaded and initialized from TransMeta.loadXML() or // KettleDatabaseRepositoryTransDelegate.loadTransformation() // so the StepMeta references are wired up in the MappingMeta properly // (Copied from TransMeta.loadXML()) for ( int i = 0; i < transMeta.nrSteps(); i++ ) { StepMeta stepMeta = transMeta.getStep( i ); StepMetaInterface sii = stepMeta.getStepMetaInterface(); if ( sii != null ) { sii.searchInfoAndTargetSteps( transMeta.getSteps() ); } } // Verify the transformation was configured properly assertEquals( "Transformation not initialized properly", 2, transMeta.nrSteps() ); StepMeta meta = transMeta.getStep( 1 ); assertTrue( "Transformation not initialized properly", meta.getStepMetaInterface() instanceof MappingMeta ); MappingMeta loadedMappingMeta = (MappingMeta) meta.getStepMetaInterface(); assertEquals( "Expected a single input mapping definition", 1, loadedMappingMeta.getInputMappings().size() ); StepIOMetaInterface ioMeta = loadedMappingMeta.getStepIOMeta(); assertEquals( "Expected a single Info Stream", 1, ioMeta.getInfoStreams().size() ); assertEquals( "Expected a single Info Step", 1, loadedMappingMeta.getInfoSteps().length ); // Verify the transformation can be executed StepInterface si = trans.getStepInterface( mappingName, 0 ); RowStepCollector rc = new RowStepCollector(); si.addRowListener( rc ); trans.startThreads(); trans.waitUntilFinished(); assertEquals( 1, rc.getRowsRead().size() ); assertEquals( 1, rc.getRowsWritten().size() ); } /** * Tests that an input step that is a main data path is not flagged as an info stream */ public void testInfoStreams_with_main_data_path() throws Exception { KettleEnvironment.init(); PluginRegistry registry = PluginRegistry.getInstance(); // // Create a new transformation with a row generator that feeds a Mapping (Sub-Transformation) Step // TransMeta transMeta = new TransMeta(); transMeta.setName( "Mapping Info Test" ); StepMeta rowGenerator = buildRowGeneratorStep( registry, "Generate Rows" ); transMeta.addStep( rowGenerator ); StepMeta rowGeneratorMain = buildRowGeneratorStep( registry, "Generate Rows Main" ); transMeta.addStep( rowGeneratorMain ); String mappingName = "mapping"; MappingMeta mappingMeta = new MappingMeta(); mappingMeta.setSpecificationMethod( ObjectLocationSpecificationMethod.FILENAME ); mappingMeta.setFileName( "test/org/pentaho/di/trans/steps/mapping/subtrans.ktr" ); List<MappingIODefinition> inputMappings = new ArrayList<MappingIODefinition>(); String mappingInputStepName = "input"; inputMappings.add( createMappingDef( rowGenerator.getName(), mappingInputStepName, "string", "a" ) ); // Create the main data path mapping MappingIODefinition mainMappingDef = createMappingDef( rowGeneratorMain.getName(), mappingInputStepName, "string", "a" ); mainMappingDef.setMainDataPath( true ); inputMappings.add( mainMappingDef ); mappingMeta.setInputMappings( inputMappings ); String mappingPid = registry.getPluginId( StepPluginType.class, mappingMeta ); StepMeta mapping = new StepMeta( mappingPid, mappingName, mappingMeta ); transMeta.addStep( mapping ); TransHopMeta hopGeneratorToMapping = new TransHopMeta( rowGenerator, mapping ); transMeta.addTransHop( hopGeneratorToMapping ); hopGeneratorToMapping = new TransHopMeta( rowGeneratorMain, mapping ); transMeta.addTransHop( hopGeneratorToMapping ); Trans trans = new Trans( transMeta ); trans.prepareExecution( null ); // Mimic how a transformation is loaded and initialized from TransMeta.loadXML() or // KettleDatabaseRepositoryTransDelegate.loadTransformation() // so the StepMeta references are wired up in the MappingMeta properly // (Copied from TransMeta.loadXML()) for ( int i = 0; i < transMeta.nrSteps(); i++ ) { StepMeta stepMeta = transMeta.getStep( i ); StepMetaInterface sii = stepMeta.getStepMetaInterface(); if ( sii != null ) { sii.searchInfoAndTargetSteps( transMeta.getSteps() ); } } // Verify the transformation was configured properly assertEquals( "Transformation not initialized properly", 3, transMeta.nrSteps() ); StepMeta meta = transMeta.getStep( 2 ); assertTrue( "Transformation not initialized properly", meta.getStepMetaInterface() instanceof MappingMeta ); MappingMeta loadedMappingMeta = (MappingMeta) meta.getStepMetaInterface(); assertEquals( "Expected a two input mapping definition", 2, loadedMappingMeta.getInputMappings().size() ); StepIOMetaInterface ioMeta = loadedMappingMeta.getStepIOMeta(); assertEquals( "Expected a single Info Stream", 1, ioMeta.getInfoStreams().size() ); assertEquals( "Expected a single Info Step", 1, loadedMappingMeta.getInfoSteps().length ); } public void testMapping_WhenSharingPreviousStepWithAnother() throws Exception { KettleEnvironment.init(); TransMeta transMeta = new TransMeta( "testfiles/org/pentaho/di/trans/steps/mapping/pdi-13435/PDI-13435-main.ktr" ); transMeta.setTransformationType( TransMeta.TransformationType.Normal ); Trans trans = new Trans( transMeta ); trans.prepareExecution( null ); trans.startThreads(); trans.waitUntilFinished(); assertEquals( 0, trans.getErrors() ); } /** * This test case relates to PDI-13545. It executes a transformation with a Mapping step that is not configured * manually with an <tt>outputStepname</tt> property and, therefore, it is set to be a mapping output step from the * internal transformation. * * @throws Exception */ public void testMapping_WhenNextStepHasTwoCopies_AndOutputIsNotDefinedExplicitly() throws Exception { runTransWhenMappingsIsFollowedByCopiedStep( "testfiles/org/pentaho/di/trans/steps/mapping/pdi-13545/pdi-13545-1.ktr" ); } /** * This test case relates to PDI-13545. It executes a transformation with a Mapping step that is configured manually * with an <tt>outputStepname</tt> property. * * @throws Exception */ public void testMapping_WhenNextStepHasTwoCopies_AndOutputIsDefinedExplicitly() throws Exception { runTransWhenMappingsIsFollowedByCopiedStep( "testfiles/org/pentaho/di/trans/steps/mapping/pdi-13545/pdi-13545-2.ktr" ); } /** * This method runs transformations related to PDI-13545.<br/> The scenario is the following: there are two step * generating data, the latter of which is a Mapping step. They are followed with a Join Rows step, that has two * copies. The last in a row is a Dummy step, named "Last". Since both generating steps output 3 rows ([10, 20, 30] * and [1, 2, 3] respectively), the last step must obtain 3*3=9 rows. * * @param transPath a path to transformation file * @throws Exception */ private void runTransWhenMappingsIsFollowedByCopiedStep( String transPath ) throws Exception { KettleEnvironment.init(); TransMeta transMeta = new TransMeta( transPath ); transMeta.setTransformationType( TransMeta.TransformationType.Normal ); Trans trans = new Trans( transMeta ); trans.prepareExecution( null ); trans.startThreads(); trans.waitUntilFinished(); assertEquals( 0, trans.getErrors() ); List<StepInterface> list = trans.findBaseSteps( "Last" ); assertEquals( 1, list.size() ); assertEquals( 9, list.get( 0 ).getLinesRead() ); } }