/*! ****************************************************************************** * * 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.core.row; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.pentaho.di.core.KettleClientEnvironment; import org.pentaho.di.core.exception.KettlePluginException; import org.pentaho.di.core.exception.KettleValueException; import org.pentaho.di.core.row.value.ValueMetaBase; import org.pentaho.di.core.row.value.ValueMetaFactory; import org.pentaho.di.core.row.value.ValueMetaInteger; import org.pentaho.di.core.row.value.ValueMetaString; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class RowMetaTest { RowMetaInterface rowMeta = new RowMeta(); ValueMetaInterface string; ValueMetaInterface integer; ValueMetaInterface date; ValueMetaInterface charly; ValueMetaInterface dup; @BeforeClass public static void setUpBeforeClass() throws Exception { KettleClientEnvironment.init(); } @AfterClass public static void tearDownAfterClass() throws Exception { } @Before public void setUp() throws Exception { string = ValueMetaFactory.createValueMeta( "string", ValueMetaInterface.TYPE_STRING ); rowMeta.addValueMeta( string ); integer = ValueMetaFactory.createValueMeta( "integer", ValueMetaInterface.TYPE_INTEGER ); rowMeta.addValueMeta( integer ); date = ValueMetaFactory.createValueMeta( "date", ValueMetaInterface.TYPE_DATE ); rowMeta.addValueMeta( date ); charly = ValueMetaFactory.createValueMeta( "charly", ValueMetaInterface.TYPE_SERIALIZABLE ); dup = ValueMetaFactory.createValueMeta( "dup", ValueMetaInterface.TYPE_SERIALIZABLE ); } private List<ValueMetaInterface> generateVList( String[] names, int[] types ) throws KettlePluginException { List<ValueMetaInterface> list = new ArrayList<ValueMetaInterface>(); for ( int i = 0; i < names.length; i++ ) { ValueMetaInterface vm = ValueMetaFactory.createValueMeta( names[i], types[i] ); vm.setOrigin( "originStep" ); list.add( vm ); } return list; } @Test public void testGetValueMetaList() { List<ValueMetaInterface> list = rowMeta.getValueMetaList(); assertTrue( list.contains( string ) ); assertTrue( list.contains( integer ) ); assertTrue( list.contains( date ) ); } @Test public void testSetValueMetaList() throws KettlePluginException { List<ValueMetaInterface> setList = this.generateVList( new String[]{ "alpha", "bravo" }, new int[]{ 2, 2 } ); rowMeta.setValueMetaList( setList ); assertTrue( setList.contains( rowMeta.searchValueMeta( "alpha" ) ) ); assertTrue( setList.contains( rowMeta.searchValueMeta( "bravo" ) ) ); // check that it is avalable by index: assertEquals( 0, rowMeta.indexOfValue( "alpha" ) ); assertEquals( 1, rowMeta.indexOfValue( "bravo" ) ); } @Test public void testSetValueMetaListNullName() throws KettlePluginException { List<ValueMetaInterface> setList = this.generateVList( new String[]{ "alpha", null }, new int[]{ 2, 2 } ); rowMeta.setValueMetaList( setList ); assertTrue( setList.contains( rowMeta.searchValueMeta( "alpha" ) ) ); assertFalse( setList.contains( rowMeta.searchValueMeta( null ) ) ); // check that it is avalable by index: assertEquals( 0, rowMeta.indexOfValue( "alpha" ) ); assertEquals( -1, rowMeta.indexOfValue( null ) ); } @Test( expected = UnsupportedOperationException.class ) public void testDeSynchronizationModifyingOriginalList() { // remember 0-based arrays int size = rowMeta.size(); // should be added at the end rowMeta.getValueMetaList().add( charly ); assertEquals( size, rowMeta.indexOfValue( "charly" ) ); } @Test public void testExists() { assertTrue( rowMeta.exists( string ) ); assertTrue( rowMeta.exists( date ) ); assertTrue( rowMeta.exists( integer ) ); } @Test public void testAddValueMetaValueMetaInterface() throws KettlePluginException { rowMeta.addValueMeta( charly ); assertTrue( rowMeta.getValueMetaList().contains( charly ) ); } @Test public void testAddValueMetaNullName() throws KettlePluginException { ValueMetaInterface vmi = new ValueMetaBase(); rowMeta.addValueMeta( vmi ); assertTrue( rowMeta.getValueMetaList().contains( vmi ) ); } @Test public void testAddValueMetaIntValueMetaInterface() throws KettlePluginException { rowMeta.addValueMeta( 1, charly ); assertTrue( rowMeta.getValueMetaList().indexOf( charly ) == 1 ); } @Test public void testGetValueMeta() { // see before method insertion order. assertTrue( rowMeta.getValueMeta( 1 ).equals( integer ) ); } @Test public void testSetValueMeta() throws KettlePluginException { rowMeta.setValueMeta( 1, charly ); assertEquals( 1, rowMeta.getValueMetaList().indexOf( charly ) ); assertEquals( "There is still 3 elements:", 3, rowMeta.size() ); assertEquals( -1, rowMeta.indexOfValue( "integer" ) ); } @Test public void testSetValueMetaDup() throws KettlePluginException { rowMeta.setValueMeta( 1, dup ); assertEquals( "There is still 3 elements:", 3, rowMeta.size() ); assertEquals( -1, rowMeta.indexOfValue( "integer" ) ); rowMeta.setValueMeta( 1, dup ); assertEquals( "There is still 3 elements:", 3, rowMeta.size() ); assertEquals( -1, rowMeta.indexOfValue( "integer" ) ); rowMeta.setValueMeta( 2, dup ); assertEquals( "There is still 3 elements:", 3, rowMeta.size() ); assertEquals( "Original is still the same (object)", 1, rowMeta.getValueMetaList().indexOf( dup ) ); assertEquals( "Original is still the same (name)", 1, rowMeta.indexOfValue( "dup" ) ); assertEquals( "Renaming happened", 2, rowMeta.indexOfValue( "dup_1" ) ); } @Test public void testSetValueMetaNullName() throws KettlePluginException { ValueMetaInterface vmi = new ValueMetaBase(); rowMeta.setValueMeta( 1, vmi ); assertEquals( 1, rowMeta.getValueMetaList().indexOf( vmi ) ); assertEquals( "There is still 3 elements:", 3, rowMeta.size() ); } @Test public void testIndexOfValue() { List<ValueMetaInterface> list = rowMeta.getValueMetaList(); assertEquals( 0, list.indexOf( string ) ); assertEquals( 1, list.indexOf( integer ) ); assertEquals( 2, list.indexOf( date ) ); } @Test public void testIndexOfNullValue() { assertEquals( -1, rowMeta.indexOfValue( null ) ); } @Test public void testSearchValueMeta() { ValueMetaInterface vmi = rowMeta.searchValueMeta( "integer" ); assertEquals( integer, vmi ); vmi = rowMeta.searchValueMeta( "string" ); assertEquals( string, vmi ); vmi = rowMeta.searchValueMeta( "date" ); assertEquals( date, vmi ); } @Test public void testAddRowMeta() throws KettlePluginException { List<ValueMetaInterface> list = this.generateVList( new String[]{ "alfa", "bravo", "charly", "delta" }, new int[]{ 2, 2, 3, 4 } ); RowMeta added = new RowMeta(); added.setValueMetaList( list ); rowMeta.addRowMeta( added ); assertEquals( 7, rowMeta.getValueMetaList().size() ); assertEquals( 5, rowMeta.indexOfValue( "charly" ) ); } @Test public void testMergeRowMeta() throws KettlePluginException { List<ValueMetaInterface> list = this.generateVList( new String[]{ "phobos", "demos", "mars" }, new int[]{ 6, 6, 6 } ); list.add( 1, integer ); RowMeta toMerge = new RowMeta(); toMerge.setValueMetaList( list ); rowMeta.mergeRowMeta( toMerge ); assertEquals( 7, rowMeta.size() ); list = rowMeta.getValueMetaList(); assertTrue( list.contains( integer ) ); ValueMetaInterface found = null; for ( ValueMetaInterface vm : list ) { if ( vm.getName().equals( "integer_1" ) ) { found = vm; break; } } assertNotNull( found ); } @Test public void testRemoveValueMetaString() throws KettleValueException { rowMeta.removeValueMeta( "string" ); assertEquals( 2, rowMeta.size() ); assertNotNull( rowMeta.searchValueMeta( "integer" ) ); assertTrue( rowMeta.searchValueMeta( "integer" ).getName().equals( "integer" ) ); assertNull( rowMeta.searchValueMeta( "string" ) ); } @Test public void testRemoveValueMetaInt() { rowMeta.removeValueMeta( 1 ); assertEquals( 2, rowMeta.size() ); assertNotNull( rowMeta.searchValueMeta( "date" ) ); assertNotNull( rowMeta.searchValueMeta( "string" ) ); assertNull( rowMeta.searchValueMeta( "notExists" ) ); assertTrue( rowMeta.searchValueMeta( "date" ).getName().equals( "date" ) ); assertNull( rowMeta.searchValueMeta( "integer" ) ); } @Test public void testLowerCaseNamesSearch() { assertNotNull( rowMeta.searchValueMeta( "Integer" ) ); assertNotNull( rowMeta.searchValueMeta( "string".toUpperCase() ) ); } @Test public void testMultipleSameNameInserts() { for ( int i = 0; i < 13; i++ ) { rowMeta.addValueMeta( integer ); } String resultName = "integer_13"; assertTrue( rowMeta.searchValueMeta( resultName ).getName().equals( resultName ) ); } @Test public void testExternalValueMetaModification() { ValueMetaInterface vmi = rowMeta.searchValueMeta( "string" ); vmi.setName( "string2" ); assertNotNull( rowMeta.searchValueMeta( vmi.getName() ) ); } @Test public void testSwapNames() throws KettlePluginException { ValueMetaInterface string2 = ValueMetaFactory.createValueMeta( "string2", ValueMetaInterface.TYPE_STRING ); rowMeta.addValueMeta( string2 ); assertSame( string, rowMeta.searchValueMeta( "string" ) ); assertSame( string2, rowMeta.searchValueMeta( "string2" ) ); string.setName( "string2" ); string2.setName( "string" ); assertSame( string2, rowMeta.searchValueMeta( "string" ) ); assertSame( string, rowMeta.searchValueMeta( "string2" ) ); } @Test public void testCopyRowMetaCacheConstructor() { Map<String, Integer> mapping = new HashMap<>(); mapping.put( "a", 1 ); List<Integer> needRealClone = new ArrayList<>(); needRealClone.add( 2 ); RowMeta.RowMetaCache rowMetaCache = new RowMeta.RowMetaCache( mapping, needRealClone ); RowMeta.RowMetaCache rowMetaCache2 = new RowMeta.RowMetaCache( rowMetaCache ); assertEquals( rowMetaCache.mapping, rowMetaCache2.mapping ); assertEquals( rowMetaCache.needRealClone, rowMetaCache2.needRealClone ); rowMetaCache = new RowMeta.RowMetaCache( mapping, null ); rowMetaCache2 = new RowMeta.RowMetaCache( rowMetaCache ); assertEquals( rowMetaCache.mapping, rowMetaCache2.mapping ); assertNull( rowMetaCache2.needRealClone ); } // @Test public void hasedRowMetaListFasterWhenSearchByName() throws KettlePluginException { rowMeta.clear(); ValueMetaInterface searchFor = null; for ( int i = 0; i < 100000; i++ ) { ValueMetaInterface vm = ValueMetaFactory.createValueMeta( UUID.randomUUID().toString(), ValueMetaInterface.TYPE_STRING ); rowMeta.addValueMeta( vm ); if ( i == 50000 ) { searchFor = vm; } } List<ValueMetaInterface> vmList = rowMeta.getValueMetaList(); // now see how fast we are. long start, stop, time1, time2; start = System.nanoTime(); vmList.indexOf( searchFor ); stop = System.nanoTime(); time1 = stop - start; start = System.nanoTime(); ValueMetaInterface found = rowMeta.searchValueMeta( searchFor.getName() ); stop = System.nanoTime(); assertEquals( searchFor, found ); time2 = stop - start; // System.out.println( time1 + ", " + time2 ); assertTrue( "array search is slower then current implementation : " + "for array list: " + time1 + ", for hashed rowMeta: " + time2, time1 > time2 ); } // @Test public void hashedRowMetaListNotMuchSlowerThenIndexedAccess() throws KettlePluginException { rowMeta = new RowMeta(); // create pre-existed rom meta list List<ValueMetaInterface> pre = new ArrayList<ValueMetaInterface>( 100000 ); for ( int i = 0; i < 100000; i++ ) { ValueMetaInterface vm = ValueMetaFactory.createValueMeta( UUID.randomUUID().toString(), ValueMetaInterface.TYPE_STRING ); pre.add( vm ); } // now see how fast we are. long start, stop, time1, time2; start = System.nanoTime(); // this is when filling regular array like in prev implementation List<ValueMetaInterface> prev = new ArrayList<ValueMetaInterface>(); for ( ValueMetaInterface item : pre ) { prev.add( item ); } stop = System.nanoTime(); time1 = stop - start; start = System.nanoTime(); for ( ValueMetaInterface item : pre ) { rowMeta.addValueMeta( item ); } stop = System.nanoTime(); time2 = stop - start; // ~6 time slower that for original implementation System.out.println( time1 + ", " + time2 ); // let say finally it is not 10 times slower :( assertTrue( "it is not 10 times slower than for original arrayList", time1 * 10 > time2 ); } @Test public void testMergeRowMetaWithOriginStep() throws Exception { List<ValueMetaInterface> list = this.generateVList( new String[]{ "phobos", "demos", "mars" }, new int[]{ 6, 6, 6 } ); list.add( 1, integer ); RowMeta toMerge = new RowMeta(); toMerge.setValueMetaList( list ); rowMeta.mergeRowMeta( toMerge, "newOriginStep" ); assertEquals( 7, rowMeta.size() ); list = rowMeta.getValueMetaList(); assertTrue( list.contains( integer ) ); ValueMetaInterface found = null; ValueMetaInterface other = null; for ( ValueMetaInterface vm : list ) { if ( vm.getName().equals( "integer_1" ) ) { found = vm; break; } else { other = vm; } } assertNotNull( found ); assertEquals( found.getOrigin(), "newOriginStep" ); assertNotNull( other ); assertEquals( other.getOrigin(), "originStep" ); } @Test public void testGetFieldNames() { rowMeta.clear(); fillRowMeta(); String[] names = rowMeta.getFieldNames(); assertEquals( 10, names.length ); assertEquals( "sample", names[0] ); for ( int i = 1; i < names.length; i++ ) { assertEquals( "", names[i] ); } } private void fillRowMeta() { rowMeta.addValueMeta( 0, new ValueMetaString( "sample" ) ); for ( int i = 1; i < 10; i++ ) { rowMeta.addValueMeta( i, new ValueMetaInteger( null ) ); } } }