You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ol...@apache.org on 2020/05/27 01:35:26 UTC
[maven-shared-utils] 01/01: [MSHARED-884] - Add tests for existing
FileUtils.copyFile() method with no filtering.
This is an automated email from the ASF dual-hosted git repository.
olamy pushed a commit to branch pr-28
in repository https://gitbox.apache.org/repos/asf/maven-shared-utils.git
commit e57180309a64758e161513d80f1185e13dc84826
Author: Rob Oxspring <ro...@imapmail.org>
AuthorDate: Fri May 1 00:35:56 2020 +0100
[MSHARED-884] - Add tests for existing FileUtils.copyFile() method with no filtering.
- Add tests for existing FileUtils.copyFile() method with filtering.
- Refactor to deal with the simpler no-filters case first
- Only overwrite existing file if there's a content change
- Avoid conflicts with Java 9 additional Buffer methods
- Consistent explicit use of Charset instances
- Prefer Files.newBufferedReader/Writer methods
- Use TimeUnit rather than constants in millisecond
---
.../apache/maven/shared/utils/io/FileUtils.java | 164 ++++++++---
.../maven/shared/utils/io/FileUtilsTest.java | 321 ++++++++++++++++++++-
2 files changed, 426 insertions(+), 59 deletions(-)
diff --git a/src/main/java/org/apache/maven/shared/utils/io/FileUtils.java b/src/main/java/org/apache/maven/shared/utils/io/FileUtils.java
index f7de64c..8929c4d 100644
--- a/src/main/java/org/apache/maven/shared/utils/io/FileUtils.java
+++ b/src/main/java/org/apache/maven/shared/utils/io/FileUtils.java
@@ -30,17 +30,20 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
+import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
import java.nio.file.Files;
import java.security.SecureRandom;
import java.text.DecimalFormat;
@@ -110,7 +113,7 @@ public class FileUtils
/**
* The file copy buffer size (30 MB)
*/
- private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
+ private static final int FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
/**
* The vm line separator
@@ -271,13 +274,12 @@ public class FileUtils
@Nonnull public static String fileRead( @Nonnull File file, @Nullable String encoding )
throws IOException
{
- StringBuilder buf = new StringBuilder();
- if ( encoding == null )
- {
- encoding = Charset.defaultCharset().name();
- }
+ Charset charset = charset( encoding );
+
+ StringBuilder buf = new StringBuilder();
+
- try ( Reader reader = new InputStreamReader( new FileInputStream( file ), encoding ) )
+ try ( Reader reader = Files.newBufferedReader( file.toPath(), charset ) )
{
int count;
char[] b = new char[512];
@@ -329,15 +331,11 @@ public class FileUtils
public static void fileAppend( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data )
throws IOException
{
-
- if ( encoding == null )
- {
- encoding = Charset.defaultCharset().name();
- }
-
+ Charset charset = charset( encoding );
+
try ( OutputStream out = new FileOutputStream( fileName, true ) )
{
- out.write( data.getBytes( encoding ) );
+ out.write( data.getBytes( charset ) );
}
}
@@ -381,13 +379,9 @@ public class FileUtils
public static void fileWrite( @Nonnull File file, @Nullable String encoding, @Nonnull String data )
throws IOException
{
-
- if ( encoding == null )
- {
- encoding = Charset.defaultCharset().name();
- }
+ Charset charset = charset( encoding );
- try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), encoding ) )
+ try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) )
{
writer.write( data );
}
@@ -418,13 +412,9 @@ public class FileUtils
public static void fileWriteArray( @Nonnull File file, @Nullable String encoding, @Nullable String... data )
throws IOException
{
-
- if ( encoding == null )
- {
- encoding = Charset.defaultCharset().name();
- }
+ Charset charset = charset( encoding );
- try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), encoding ) )
+ try ( Writer writer = Files.newBufferedWriter( file.toPath(), charset ) )
{
for ( int i = 0; data != null && i < data.length; i++ )
{
@@ -1810,34 +1800,97 @@ public class FileUtils
@Nullable FilterWrapper[] wrappers, boolean overwrite )
throws IOException
{
- if ( wrappers != null && wrappers.length > 0 )
+ if ( wrappers == null || wrappers.length == 0 )
{
-
- if ( encoding == null || encoding.isEmpty() )
+ if ( overwrite || to.lastModified() < from.lastModified() )
{
- encoding = Charset.defaultCharset().name();
+ copyFile( from, to );
}
-
+ }
+ else
+ {
+ Charset charset = charset( encoding );
+
// buffer so it isn't reading a byte at a time!
- try ( Reader fileReader =
- new BufferedReader( new InputStreamReader( new FileInputStream( from ), encoding ) );
- Writer fileWriter = new OutputStreamWriter( new FileOutputStream( to ), encoding ) )
+ try ( Reader fileReader = Files.newBufferedReader( from.toPath(), charset ) )
{
-
Reader wrapped = fileReader;
for ( FilterWrapper wrapper : wrappers )
{
wrapped = wrapper.getReader( wrapped );
}
- IOUtil.copy( wrapped, fileWriter );
- }
- }
- else
- {
- if ( to.lastModified() < from.lastModified() || overwrite )
- {
- copyFile( from, to );
+ if ( overwrite || !to.exists() )
+ {
+ try ( Writer fileWriter = Files.newBufferedWriter( to.toPath(), charset ) )
+ {
+ IOUtil.copy( wrapped, fileWriter );
+ }
+ }
+ else
+ {
+ CharsetEncoder encoder = charset.newEncoder();
+
+ int totalBufferSize = FILE_COPY_BUFFER_SIZE;
+
+ int charBufferSize = ( int ) Math.floor( totalBufferSize / ( 2 + 2 * encoder.maxBytesPerChar() ) );
+ int byteBufferSize = ( int ) Math.ceil( charBufferSize * encoder.maxBytesPerChar() );
+
+ CharBuffer newChars = CharBuffer.allocate( charBufferSize );
+ ByteBuffer newBytes = ByteBuffer.allocate( byteBufferSize );
+ ByteBuffer existingBytes = ByteBuffer.allocate( byteBufferSize );
+
+ CoderResult coderResult;
+ int existingRead;
+ boolean writing = false;
+
+ try ( final RandomAccessFile existing = new RandomAccessFile( to, "rw" ) )
+ {
+ int n;
+ while ( -1 != ( n = wrapped.read( newChars ) ) )
+ {
+ ( ( Buffer ) newChars ).flip();
+
+ coderResult = encoder.encode( newChars, newBytes, n != 0 );
+ if ( coderResult.isError() )
+ {
+ coderResult.throwException();
+ }
+
+ ( ( Buffer ) newBytes ).flip();
+
+ if ( !writing )
+ {
+ existingRead = existing.read( existingBytes.array(), 0, newBytes.remaining() );
+ ( ( Buffer ) existingBytes ).position( existingRead );
+ ( ( Buffer ) existingBytes ).flip();
+
+ if ( newBytes.compareTo( existingBytes ) != 0 )
+ {
+ writing = true;
+ if ( existingRead > 0 )
+ {
+ existing.seek( existing.getFilePointer() - existingRead );
+ }
+ }
+ }
+
+ if ( writing )
+ {
+ existing.write( newBytes.array(), 0, newBytes.remaining() );
+ }
+
+ ( ( Buffer ) newChars ).clear();
+ ( ( Buffer ) newBytes ).clear();
+ ( ( Buffer ) existingBytes ).clear();
+ }
+
+ if ( existing.length() > existing.getFilePointer() )
+ {
+ existing.setLength( existing.getFilePointer() );
+ }
+ }
+ }
}
}
}
@@ -1856,7 +1909,7 @@ public class FileUtils
if ( file.exists() )
{
- try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) )
+ try ( BufferedReader reader = Files.newBufferedReader( file.toPath(), Charset.defaultCharset() ) )
{
for ( String line = reader.readLine(); line != null; line = reader.readLine() )
{
@@ -1874,6 +1927,23 @@ public class FileUtils
}
/**
+ * Returns the named charset or the default charset.
+ * @param encoding the name or alias of the charset, null or empty
+ * @return A charset object for the named or default charset.
+ */
+ private static Charset charset( String encoding )
+ {
+ if ( encoding == null || encoding.isEmpty() )
+ {
+ return Charset.defaultCharset();
+ }
+ else
+ {
+ return Charset.forName( encoding );
+ }
+ }
+
+ /**
* For Windows OS, check if the file name contains any of the following characters:
* <code>":", "*", "?", "\"", "<", ">", "|"</code>
*
diff --git a/src/test/java/org/apache/maven/shared/utils/io/FileUtilsTest.java b/src/test/java/org/apache/maven/shared/utils/io/FileUtilsTest.java
index 69e72a9..24bf43d 100644
--- a/src/test/java/org/apache/maven/shared/utils/io/FileUtilsTest.java
+++ b/src/test/java/org/apache/maven/shared/utils/io/FileUtilsTest.java
@@ -14,19 +14,38 @@ import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
+import org.apache.maven.shared.utils.Os;
+import org.apache.maven.shared.utils.testhelpers.FileTestHelper;
+import org.codehaus.plexus.util.InterpolationFilterReader;
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+
+import javax.annotation.Nonnull;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
import java.net.URL;
import java.nio.file.Files;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -47,16 +66,6 @@ import java.util.List;
* under the License.
*/
-import org.apache.maven.shared.utils.Os;
-import org.apache.maven.shared.utils.testhelpers.FileTestHelper;
-import org.hamcrest.CoreMatchers;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.rules.TestName;
-
/**
* This is used to test FileUtils for correctness.
*
@@ -441,10 +450,299 @@ public class FileUtilsTest
testFile1.lastModified() == destination.lastModified());*/
}
+ /** A time today, rounded down to the previous minute */
+ private static long MODIFIED_TODAY = (System.currentTimeMillis() / TimeUnit.MINUTES.toMillis( 1 )) * TimeUnit.MINUTES.toMillis( 1 );
+
+ /** A time yesterday, rounded down to the previous minute */
+ private static long MODIFIED_YESTERDAY = MODIFIED_TODAY - TimeUnit.DAYS.toMillis( 1 );
+
+ /** A time last week, rounded down to the previous minute */
+ private static long MODIFIED_LAST_WEEK = MODIFIED_TODAY - TimeUnit.DAYS.toMillis( 7 );
+
@Test
- public void copyFileThatIsSymlink()
+ public void copyFileWithNoFiltersAndNoDestination()
throws Exception
{
+ File from = write(
+ "from.txt",
+ MODIFIED_YESTERDAY,
+ "Hello World!"
+ );
+ File to = new File(
+ tempFolder.getRoot(),
+ "to.txt"
+ );
+
+ FileUtils.copyFile( from, to, null, ( FileUtils.FilterWrapper[] ) null);
+
+ assertTrue(
+ "to.txt did not exist so should have been written",
+ to.lastModified() >= MODIFIED_TODAY
+ );
+ assertFileContent( to, "Hello World!" );
+ }
+
+ @Test
+ public void copyFileWithNoFiltersAndOutdatedDestination()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_YESTERDAY,
+ "Hello World!"
+ );
+ File to = write(
+ "to.txt",
+ MODIFIED_LAST_WEEK,
+ "Older content"
+ );
+
+ FileUtils.copyFile( from, to, null, ( FileUtils.FilterWrapper[] ) null);
+
+ assertTrue(
+ "to.txt was outdated so should have been overwritten",
+ to.lastModified() >= MODIFIED_TODAY
+ );
+ assertFileContent( to, "Hello World!" );
+ }
+
+ @Test
+ public void copyFileWithNoFiltersAndNewerDestination()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_LAST_WEEK,
+ "Hello World!"
+ );
+ File to = write(
+ "to.txt",
+ MODIFIED_YESTERDAY,
+ "Older content"
+ );
+
+ FileUtils.copyFile( from, to, null, ( FileUtils.FilterWrapper[] ) null);
+
+ assertTrue(
+ "to.txt was newer so should have been left alone",
+ to.lastModified() < MODIFIED_TODAY
+ );
+ assertFileContent( to, "Older content" );
+ }
+
+ @Test
+ public void copyFileWithNoFiltersAndNewerDestinationButForcedOverwrite()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_LAST_WEEK,
+ "Hello World!"
+ );
+ File to = write(
+ "to.txt",
+ MODIFIED_YESTERDAY,
+ "Older content"
+ );
+
+ FileUtils.copyFile( from, to, null, null, true);
+
+ assertTrue(
+ "to.txt was newer but the overwrite should have been forced",
+ to.lastModified() >= MODIFIED_TODAY
+ );
+ assertFileContent( to, "Hello World!" );
+ }
+
+ @Test
+ public void copyFileWithFilteringButNoFilters()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_YESTERDAY,
+ "Hello ${name}!"
+ );
+ File to = write(
+ "to.txt",
+ MODIFIED_LAST_WEEK,
+ "Older content"
+ );
+
+ FileUtils.copyFile( from, to, null );
+
+ assertTrue(
+ "to.txt was outdated so should have been overwritten",
+ to.lastModified() >= MODIFIED_TODAY
+ );
+ assertFileContent( to, "Hello ${name}!" );
+ }
+
+ @Test
+ public void copyFileWithFilteringAndNoDestination()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_YESTERDAY,
+ "Hello ${name}!"
+ );
+ File to = new File(
+ tempFolder.getRoot(),
+ "to.txt"
+ );
+
+ FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ) );
+
+ assertTrue(
+ "to.txt did not exist so should have been written",
+ to.lastModified() >= MODIFIED_TODAY
+ );
+ assertFileContent( to, "Hello Bob!" );
+ }
+
+ @Test
+ public void copyFileWithFilteringAndOutdatedDestination()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_YESTERDAY,
+ "Hello ${name}!"
+ );
+ File to = write(
+ "to.txt",
+ MODIFIED_LAST_WEEK,
+ "Older content"
+ );
+
+ FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ) );
+
+ assertTrue(
+ "to.txt was outdated so should have been overwritten",
+ to.lastModified() >= MODIFIED_TODAY
+ );
+ assertFileContent( to, "Hello Bob!" );
+ }
+
+ @Test
+ public void copyFileWithFilteringAndNewerDestinationButForcedOverwrite()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_LAST_WEEK,
+ "Hello ${name}!"
+ );
+ File to = write(
+ "to.txt",
+ MODIFIED_YESTERDAY,
+ "Older content"
+ );
+
+ FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ), true );
+
+ assertTrue(
+ "to.txt was newer but the overwrite should have been forced",
+ to.lastModified() >= MODIFIED_TODAY
+ );
+ assertFileContent( to, "Hello Bob!" );
+ }
+
+ @Test
+ public void copyFileWithFilteringAndNewerDestinationButModifiedContent()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_LAST_WEEK,
+ "Hello ${name}!"
+ );
+ File to = write(
+ "to.txt",
+ MODIFIED_YESTERDAY,
+ "Hello Charlie!"
+ );
+
+ FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ) );
+
+ assertTrue(
+ "to.txt was outdated so should have been overwritten",
+ to.lastModified() >= MODIFIED_TODAY
+ );
+ assertFileContent( to, "Hello Bob!" );
+ }
+
+ @Test
+ public void copyFileWithFilteringAndNewerDestinationAndMatchingContent()
+ throws Exception
+ {
+ File from = write(
+ "from.txt",
+ MODIFIED_LAST_WEEK,
+ "Hello ${name}!"
+ );
+ File to = write(
+ "to.txt",
+ MODIFIED_YESTERDAY,
+ "Hello Bob!"
+ );
+
+ String encoding = null;
+
+ FileUtils.copyFile( from, to, null, wrappers( "name", "Bob" ) );
+
+ assertFileContent( to, "Hello Bob!" );
+ assertTrue(
+ "to.txt content should be unchanged and have been left alone",
+ to.lastModified() < MODIFIED_TODAY
+ );
+ }
+
+ private FileUtils.FilterWrapper[] wrappers( String key, String value )
+ {
+ final Map map = new HashMap();
+ map.put( key, value );
+ return new FileUtils.FilterWrapper[]
+ {
+ new FileUtils.FilterWrapper()
+ {
+ @Override
+ public Reader getReader( Reader reader )
+ {
+ return new InterpolationFilterReader( reader, map );
+ }
+ }
+ };
+ }
+
+ private File write( @Nonnull String name, long lastModified, @Nonnull String text) throws IOException
+ {
+ final File file = new File( tempFolder.getRoot(), name );
+ try ( final Writer writer = new FileWriter( file ) ) {
+ writer.write(text);
+ }
+ assertTrue( file.setLastModified( lastModified ) );
+ assertEquals( "Failed to set lastModified date on " + file.getPath(), lastModified, file.lastModified() );
+ return file;
+ }
+
+ private static void assertFileContent( @Nonnull File file, @Nonnull String expected ) throws IOException
+ {
+ try ( Reader in = new FileReader( file ))
+ {
+ assertEquals(
+ "Expected " + file.getPath() + " to contain: " + expected,
+ expected,
+ IOUtil.toString( in )
+ );
+ }
+ }
+
+ @Test
+ public void copyFileThatIsSymlink()
+ throws Exception
+ {
assumeFalse( Os.isFamily( Os.FAMILY_WINDOWS ) );
File destination = new File( tempFolder.getRoot(), "symCopy.txt" );
@@ -457,7 +755,6 @@ public class FileUtilsTest
}
-
@Test
public void deleteFile()
throws Exception