/*! ****************************************************************************** * * 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.variables; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.pentaho.di.core.exception.KettleValueException; import org.pentaho.di.core.row.RowMeta; import org.pentaho.di.core.row.value.ValueMetaString; /** * Variables tests. * * @author Yury Bakhmutski * @see Variables */ public class VariablesTest { private Variables variables = new Variables(); /** * Test for PDI-12893 issue. Checks if an ConcurrentModificationException while iterating over the System properties * is occurred. */ @Test public void testinItializeVariablesFrom() { final Variables variablesMock = mock( Variables.class ); doCallRealMethod().when( variablesMock ).initializeVariablesFrom( any( VariableSpace.class ) ); @SuppressWarnings( "unchecked" ) final Map<String, String> propertiesMock = mock( Map.class ); when( variablesMock.getProperties() ).thenReturn( propertiesMock ); doAnswer( new Answer<Map<String, String>>() { final String keyStub = "key"; @Override public Map<String, String> answer( InvocationOnMock invocation ) throws Throwable { if ( System.getProperty( keyStub ) == null ) { modifySystemproperties(); } if ( invocation.getArguments()[ 1 ] != null ) { propertiesMock.put( (String) invocation.getArguments()[ 0 ], System.getProperties().getProperty( (String) invocation.getArguments()[ 1 ] ) ); } return propertiesMock; } } ).when( propertiesMock ).put( anyString(), anyString() ); variablesMock.initializeVariablesFrom( null ); } private void modifySystemproperties() { final String keyStub = "key"; final String valueStub = "value"; Thread thread = new Thread( new Runnable() { @Override public void run() { System.setProperty( keyStub, valueStub ); } } ); thread.start(); } /** * Spawns 20 threads that modify variables to test concurrent modification error fix. * * @throws Exception */ @Test public void testConcurrentModification() throws Exception { int threads = 20; List<Callable<Boolean>> callables = new ArrayList<Callable<Boolean>>(); for ( int i = 0; i < threads; i++ ) { callables.add( newCallable() ); } // Assert threads ran successfully. for ( Future<Boolean> result : Executors.newFixedThreadPool( 5 ).invokeAll( callables ) ) { assertTrue( result.get() ); } } // Note: Not using lambda so this can be ported to older version compatible with 1.7 private Callable<Boolean> newCallable() { return new Callable<Boolean>() { @Override public Boolean call() throws Exception { for ( int i = 0; i < 300; i++ ) { String key = "key" + i; variables.setVariable( key, "value" ); assertEquals( variables.environmentSubstitute( "${" + key + "}" ), "value" ); } return true; } }; } @Test public void testFieldSubstitution() throws KettleValueException { Object[] rowData = new Object[]{ "DataOne", "DataTwo" }; RowMeta rm = new RowMeta(); rm.addValueMeta( new ValueMetaString( "FieldOne" ) ); rm.addValueMeta( new ValueMetaString( "FieldTwo" ) ); Variables vars = new Variables(); assertNull( vars.fieldSubstitute( null, rm, rowData ) ); assertEquals( "", vars.fieldSubstitute( "", rm, rowData ) ); assertEquals( "DataOne", vars.fieldSubstitute( "?{FieldOne}", rm, rowData ) ); assertEquals( "TheDataOne", vars.fieldSubstitute( "The?{FieldOne}", rm, rowData ) ); } @Test public void testEnvironmentSubstitute() { Variables vars = new Variables(); vars.setVariable( "VarOne", "DataOne" ); vars.setVariable( "VarTwo", "DataTwo" ); assertNull( vars.environmentSubstitute( (String) null ) ); assertEquals( "", vars.environmentSubstitute( "" ) ); assertEquals( "DataTwo", vars.environmentSubstitute( "${VarTwo}" ) ); assertEquals( "DataTwoEnd", vars.environmentSubstitute( "${VarTwo}End" ) ); assertEquals( 0, vars.environmentSubstitute( new String[0] ).length ); assertArrayEquals( new String[]{ "DataOne", "TheDataOne" }, vars.environmentSubstitute( new String[]{ "${VarOne}", "The${VarOne}" } ) ); } }