/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
*
* Copyright 2006 - 2016 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.reporting.platform.plugin.staging;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.pentaho.platform.api.engine.IApplicationContext;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Created by dima.prokopenko@gmail.com on 2/25/2016.
*/
public class AsyncJobFileStagingHandlerTest {
private IPentahoSession session;
private String SESSION_ID = "junit_id";
private Random rand = new Random();
private static String parentDir = System.getProperty( "java.io.tmpdir" );
private static final byte[] data = new byte[524288];
@BeforeClass public static void beforeClass() throws IOException {
assertNotNull( parentDir );
// some clean up necessary
Path temp = Paths.get( parentDir ).resolve( AsyncJobFileStagingHandler.STAGING_DIR_ATTR );
if ( temp.toFile().exists() ) {
FileUtils.deleteDirectory( temp.toFile() );
}
// temp staging directory is determined according to app context.
IApplicationContext context = mock( IApplicationContext.class );
when( context.getSolutionPath( anyString() ) ).thenReturn( parentDir );
PentahoSystem.setApplicationContext( context );
}
@Before public void before() {
session = mock( IPentahoSession.class );
when( session.getId() ).thenReturn( SESSION_ID );
rand.nextBytes( data );
AsyncJobFileStagingHandler.cleanStagingDir();
}
@Test public void testWriteToCorrectDestinationTest() throws IOException {
AsyncJobFileStagingHandler handler = new AsyncJobFileStagingHandler( session );
// - tempDir/asyncstaging/session_id
Path
tempDir =
Paths.get( parentDir ).resolve( AsyncJobFileStagingHandler.STAGING_DIR_ATTR ).resolve( session.getId() );
assertTrue( "Temp staging dir is created. (Or existed used).", tempDir.toFile().exists() );
assertTrue( "Temp staging dir is directory.", tempDir.toFile().isDirectory() );
// also created one empty file ready to write to
File[] fileList = tempDir.toFile().listFiles();
// this is temp file for report
File tempFile = fileList[0];
assertEquals( "Temp file created immediately and 0 size", 0, tempFile.length() );
// simulate async file write between requests
OutputStream out = handler.getStagingOutputStream();
IOUtils.copy( new ByteArrayInputStream( data, 0, data.length ), out );
out.close();
assertEquals( "After async execution all data written to this file.", data.length, tempFile.length() );
// this simulate when result is response output stream
// so we just copy data from staged file into response stream
IFixedSizeStreamingContent input = handler.getStagingContent();
InputStream in = input.getStream();
byte[] result = IOUtils.toByteArray( in );
IOUtils.closeQuietly( in );
assertArrayEquals( "byte wise read an writes", data, result );
assertTrue( "File not get deleted after input stream get closed", tempFile.exists() );
assertTrue( "temp dir is not deleted", tempDir.toFile().exists() );
assertTrue( input.cleanContent() );
assertFalse( "File got deleted explicitly", tempFile.exists() );
}
@Test public void testStagingDirNotGetDeletedBetweenExecutions() throws Exception {
CountDownLatch startSignal = new CountDownLatch( 0 );
int count = 30;
ExecutorService service = Executors.newFixedThreadPool( count );
List<AsyncJobFileStagingHandler> handlers = new ArrayList<>();
for ( int i = 0 ; i < count; i++ ) {
AsyncJobFileStagingHandler handler = new AsyncJobFileStagingHandler( session );
handlers.add( handler );
service.submit( new AsyncStagingReadWrite( startSignal, handler ) );
}
startSignal.countDown();
service.shutdown();
service.awaitTermination( 5, TimeUnit.SECONDS );
Path stagingDir = AsyncJobFileStagingHandler.getStagingDirPath();
File[] fileList = stagingDir.toFile().listFiles();
File sessionFolder = fileList[0];
assertTrue( sessionFolder.isDirectory() );
assertEquals( "Folder is named by session id", session.getId(), sessionFolder.getName() );
assertEquals( "Folder is NOT empty after a BACKLOG-7598 fix", 30, sessionFolder.list().length );
for ( AsyncJobFileStagingHandler handler : handlers ) {
assertTrue( handler.getStagingContent().cleanContent() );
}
assertEquals("Staging folder empty now", 0, sessionFolder.list().length );
}
static class AsyncStagingReadWrite implements Runnable {
private final CountDownLatch startSignal;
private final AsyncJobFileStagingHandler handler;
AsyncStagingReadWrite( CountDownLatch startSignal, AsyncJobFileStagingHandler handler ) {
this.startSignal = startSignal;
this.handler = handler;
}
/**
* Leak of Input/Output stream will prevent test
* from success results since as far as streams is
* open file can't be deleted successfully.
*
*/
@Override public void run() {
OutputStream out = null;
InputStream in = null;
try {
startSignal.await();
// write to
out = handler.getStagingOutputStream();
IOUtils.copy( new ByteArrayInputStream( data, 0, data.length ), out );
out.flush();
out.close();
// read from
IFixedSizeStreamingContent input = handler.getStagingContent();
in = input.getStream();
byte[] result = IOUtils.toByteArray( in );
assertArrayEquals( "byte wise read an writes", data, result );
} catch ( Exception e ) {
fail( "Unexpected exception: " + e.getClass().getName() );
} finally {
IOUtils.closeQuietly( out );
IOUtils.closeQuietly( in );
}
}
}
@AfterClass public static void afterClass() {
AsyncJobFileStagingHandler.cleanStagingDir();
PentahoSystem.shutdown();
}
}