/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*/
package com.liferay.portal.fabric.netty.fileserver;
import com.liferay.portal.fabric.netty.fileserver.handlers.FileServerTestUtil;
import com.liferay.portal.kernel.io.BigEndianCodec;
import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
import com.liferay.portal.kernel.nio.FileSystemProviderWrapper;
import com.liferay.portal.kernel.nio.FileSystemWrapper;
import com.liferay.portal.kernel.nio.PathWrapper;
import com.liferay.portal.kernel.test.CaptureHandler;
import com.liferay.portal.kernel.test.JDKLoggerTestUtil;
import com.liferay.portal.kernel.test.SwappableSecurityManager;
import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor;
import com.liferay.portal.kernel.util.JavaDetector;
import com.liferay.portal.kernel.util.ReflectionUtil;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.nio.file.spi.FileSystemProvider;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
/**
* @author Shuyang Zhou
*/
public class FileHelperUtilTest {
@ClassRule
public static final CodeCoverageAssertor codeCoverageAssertor =
CodeCoverageAssertor.INSTANCE;
@After
public void tearDown() {
FileServerTestUtil.cleanUp();
}
@Test
public void testConstructor() {
new FileHelperUtil();
}
@Test
public void testDeleteNoSuchFile() throws IOException {
Path noSuchFilePath = Paths.get("NoSuchFile");
Files.deleteIfExists(noSuchFilePath);
FileHelperUtil.delete(true, noSuchFilePath);
FileHelperUtil.delete(noSuchFilePath);
}
@Test
public void testDeleteRegularDirectoryWithRegularFile() {
Path regularDirectoryPath = Paths.get("RegularDirectory");
Path regularFilePath = regularDirectoryPath.resolve("RegularFile");
createFile(regularFilePath);
FileHelperUtil.delete(true, regularDirectoryPath);
Assert.assertTrue(Files.notExists(regularFilePath));
Assert.assertTrue(Files.notExists(regularDirectoryPath));
createFile(regularFilePath);
FileHelperUtil.delete(regularDirectoryPath);
Assert.assertTrue(Files.notExists(regularFilePath));
Assert.assertTrue(Files.notExists(regularDirectoryPath));
}
@Test
public void testDeleteRegularDirectoryWithUndeleteableFile()
throws IOException {
final IOException ioException = new IOException("Unable to delete");
Path regularDirectoryPath = Paths.get("RegularDirectory");
final Path undeleteableFilePath = regularDirectoryPath.resolve(
"UndeleteableFile");
createFile(undeleteableFilePath);
try (SwappableSecurityManager swappableSecurityManager =
new SwappableSecurityManager() {
@Override
public void checkDelete(String file) {
if (file.equals(undeleteableFilePath.toString())) {
ReflectionUtil.throwException(
new DirectoryIteratorException(ioException));
}
}
}) {
swappableSecurityManager.install();
FileHelperUtil.delete(true, regularDirectoryPath);
FileHelperUtil.delete(regularDirectoryPath);
Assert.fail();
}
catch (Exception e) {
if (JavaDetector.isJDK8()) {
Assert.assertSame(ioException, e.getCause());
}
else {
Assert.assertSame(ioException, e);
}
}
finally {
Files.delete(undeleteableFilePath);
Files.delete(regularDirectoryPath);
}
}
@Test
public void testDeleteRegularFile() {
Path regularFilePath = Paths.get("RegularFile");
createFile(regularFilePath);
FileHelperUtil.delete(true, regularFilePath);
Assert.assertTrue(Files.notExists(regularFilePath));
createFile(regularFilePath);
FileHelperUtil.delete(regularFilePath);
Assert.assertTrue(Files.notExists(regularFilePath));
}
@Test
public void testDeleteUndeleteableDirectoryWithRegularFile()
throws IOException {
final Path undeleteableDirectoryPath = Paths.get(
"UndeleteableDirectory");
Path regularFilePath = undeleteableDirectoryPath.resolve("RegularFile");
final Path newRegularFilePath = undeleteableDirectoryPath.resolve(
"NewRegularFile");
createFile(regularFilePath);
try (SwappableSecurityManager swappableSecurityManager =
new SwappableSecurityManager() {
@Override
public void checkDelete(String file) {
if (!file.equals(
undeleteableDirectoryPath.toString())) {
return;
}
createFile(newRegularFilePath);
}
}) {
swappableSecurityManager.install();
FileHelperUtil.delete(true, undeleteableDirectoryPath);
Files.delete(newRegularFilePath);
createFile(regularFilePath);
FileHelperUtil.delete(undeleteableDirectoryPath);
Assert.fail();
}
catch (Exception e) {
Assert.assertSame(DirectoryNotEmptyException.class, e.getClass());
}
finally {
Files.delete(newRegularFilePath);
Files.delete(undeleteableDirectoryPath);
}
}
@Test
public void testDeleteUndeleteableFile() throws IOException {
final IOException ioException = new IOException("Unable to delete");
final Path undeleteableFilePath = Paths.get("UndeleteableFile");
createFile(undeleteableFilePath);
try (SwappableSecurityManager swappableSecurityManager =
new SwappableSecurityManager() {
@Override
public void checkDelete(String file) {
if (file.equals(undeleteableFilePath.toString())) {
ReflectionUtil.throwException(ioException);
}
}
}) {
swappableSecurityManager.install();
FileHelperUtil.delete(true, undeleteableFilePath);
FileHelperUtil.delete(undeleteableFilePath);
Assert.fail();
}
catch (Exception e) {
Assert.assertSame(ioException, e);
}
finally {
Files.delete(undeleteableFilePath);
}
}
@Test
public void testDeleteUnreadableFile() throws IOException {
final IOException ioException = new IOException("Unable to read");
final Path unreadableFilePath = Paths.get("UnreadableFile");
createFile(unreadableFilePath);
try (SwappableSecurityManager swappableSecurityManager =
new SwappableSecurityManager() {
@Override
public void checkRead(String file) {
if (file.equals(unreadableFilePath.toString())) {
ReflectionUtil.throwException(ioException);
}
}
}) {
swappableSecurityManager.install();
FileHelperUtil.delete(true, unreadableFilePath);
FileHelperUtil.delete(unreadableFilePath);
Assert.fail();
}
catch (Exception e) {
Assert.assertSame(ioException, e);
}
finally {
Files.delete(unreadableFilePath);
}
}
@Test
public void testMoveNoSuchFile() throws IOException {
Path noSuchFilePath = Paths.get("NoSuchFile");
Path toFilePath = Paths.get("ToFile");
Files.deleteIfExists(noSuchFilePath);
try {
FileHelperUtil.move(noSuchFilePath, toFilePath, false);
Assert.fail();
}
catch (IOException ioe) {
Assert.assertSame(NoSuchFileException.class, ioe.getClass());
}
try {
FileHelperUtil.move(noSuchFilePath, toFilePath);
Assert.fail();
}
catch (IOException ioe) {
Assert.assertSame(NoSuchFileException.class, ioe.getClass());
}
}
@Test
public void testMoveRegularDirectoryWithRegularFile() throws IOException {
Path regularFromDirectoryPath = Paths.get("RegularFromDirectory");
Path regularFromFilePath = regularFromDirectoryPath.resolve(
"RegularFromFile");
Path regularToDirectoryPath = Paths.get("RegularToDirectory");
Path regularToFilePath = regularToDirectoryPath.resolve(
regularFromDirectoryPath.relativize(regularFromFilePath));
createFile(regularFromFilePath);
FileTime originalFileTime = Files.getLastModifiedTime(
regularFromDirectoryPath);
FileHelperUtil.move(
regularFromDirectoryPath, regularToDirectoryPath, false);
Assert.assertEquals(
originalFileTime,
Files.getLastModifiedTime(regularToDirectoryPath));
Assert.assertTrue(Files.notExists(regularFromFilePath));
Assert.assertTrue(Files.notExists(regularFromDirectoryPath));
Assert.assertTrue(Files.exists(regularToDirectoryPath));
Assert.assertTrue(Files.exists(regularToFilePath));
Files.delete(regularToFilePath);
Files.delete(regularToDirectoryPath);
createFile(regularFromFilePath);
originalFileTime = Files.getLastModifiedTime(regularFromDirectoryPath);
FileHelperUtil.move(regularFromDirectoryPath, regularToDirectoryPath);
Assert.assertEquals(
originalFileTime,
Files.getLastModifiedTime(regularToDirectoryPath));
Assert.assertTrue(Files.notExists(regularFromFilePath));
Assert.assertTrue(Files.notExists(regularFromDirectoryPath));
Assert.assertTrue(Files.exists(regularToDirectoryPath));
Assert.assertTrue(Files.exists(regularToFilePath));
Files.delete(regularToFilePath);
Files.delete(regularToDirectoryPath);
}
@Test
public void testMoveRegularFile() throws IOException {
Path regularFromFilePath = Paths.get("RegularFromFile");
Path regularToFilePath = Paths.get("RegularToFile");
createFile(regularFromFilePath);
FileHelperUtil.move(regularFromFilePath, regularToFilePath, false);
Assert.assertTrue(Files.notExists(regularFromFilePath));
Assert.assertTrue(Files.exists(regularToFilePath));
Files.delete(regularToFilePath);
createFile(regularFromFilePath);
FileHelperUtil.move(regularFromFilePath, regularToFilePath);
Assert.assertTrue(Files.notExists(regularFromFilePath));
Assert.assertTrue(Files.exists(regularToFilePath));
Files.delete(regularToFilePath);
}
@Test
public void testMoveRegularFileAtomicFailure() throws IOException {
final Path regularFromFilePath = Paths.get("RegularFromFile");
Path regularToFilePath = Paths.get("RegularToFile");
final AtomicMoveNotSupportedException atomicMoveNotSupportedException =
new AtomicMoveNotSupportedException(
regularFromFilePath.toString(), regularToFilePath.toString(),
"Atomic move not supported");
createFile(regularFromFilePath);
try (SwappableSecurityManager swappableSecurityManager =
new SwappableSecurityManager() {
@Override
public void checkWrite(String file) {
if (file.equals(regularFromFilePath.toString())) {
ReflectionUtil.throwException(
atomicMoveNotSupportedException);
}
}
}) {
swappableSecurityManager.install();
FileHelperUtil.move(regularFromFilePath, regularToFilePath);
}
Assert.assertTrue(Files.notExists(regularFromFilePath));
Assert.assertTrue(Files.exists(regularToFilePath));
Files.delete(regularToFilePath);
}
@Test
public void testMoveUnmoveableDirectoryWithRegularFile()
throws IOException {
final Path unmoveableFromDirectoryPath = Paths.get(
"UnmoveableDirectory");
Path regularFromFilePath = unmoveableFromDirectoryPath.resolve(
"RegularFromFile");
Path regularToDirectoryPath = Paths.get("RegularToDirectoryPath");
Path regularToFilePath = regularToDirectoryPath.resolve(
unmoveableFromDirectoryPath.relativize(regularFromFilePath));
final Path newRegularFilePath = unmoveableFromDirectoryPath.resolve(
"NewRegularFile");
createFile(regularFromFilePath);
try (SwappableSecurityManager swappableSecurityManager =
new SwappableSecurityManager() {
@Override
public void checkDelete(String file) {
if (!file.equals(
unmoveableFromDirectoryPath.toString())) {
return;
}
createFile(newRegularFilePath);
}
}) {
swappableSecurityManager.install();
FileHelperUtil.move(
unmoveableFromDirectoryPath, regularToDirectoryPath, false);
Assert.fail();
}
catch (DirectoryNotEmptyException dnee) {
}
finally {
Assert.assertTrue(Files.exists(regularToFilePath));
Assert.assertTrue(Files.exists(regularToDirectoryPath));
Assert.assertTrue(Files.notExists(regularFromFilePath));
Assert.assertTrue(Files.exists(newRegularFilePath));
Assert.assertTrue(Files.exists(unmoveableFromDirectoryPath));
Files.delete(regularToFilePath);
Files.delete(regularToDirectoryPath);
Files.delete(newRegularFilePath);
Files.delete(unmoveableFromDirectoryPath);
}
createFile(regularFromFilePath);
try (SwappableSecurityManager swappableSecurityManager =
new SwappableSecurityManager() {
@Override
public void checkDelete(String file) {
if (!file.equals(
unmoveableFromDirectoryPath.toString())) {
return;
}
createFile(newRegularFilePath);
}
}) {
swappableSecurityManager.install();
FileHelperUtil.move(
unmoveableFromDirectoryPath, regularToDirectoryPath);
Assert.fail();
}
catch (IOException ioe) {
Assert.assertEquals(
"Source path " + unmoveableFromDirectoryPath +
" was left in an inconsistent state",
ioe.getMessage());
Throwable throwable = ioe.getCause();
Assert.assertSame(
DirectoryNotEmptyException.class, throwable.getClass());
}
finally {
Assert.assertTrue(Files.notExists(regularToFilePath));
Assert.assertTrue(Files.notExists(regularToDirectoryPath));
Assert.assertTrue(Files.notExists(regularFromFilePath));
Assert.assertTrue(Files.exists(newRegularFilePath));
Assert.assertTrue(Files.exists(unmoveableFromDirectoryPath));
Files.delete(newRegularFilePath);
Files.delete(unmoveableFromDirectoryPath);
}
}
@Test
public void testUnzipCorrupttedFile() throws IOException {
Path corrupttedZipFilePath = FileServerTestUtil.registerForCleanUp(
FileServerTestUtil.createEmptyFile(Paths.get("CorrupttedZipFile")));
String fileEntryName = "CorrupttedFile";
int annotatedSize = 1024;
int actualSize = 512;
try (ZipOutputStream zipOutputStream = new ZipOutputStream(
Files.newOutputStream(corrupttedZipFilePath))) {
ZipEntry zipEntry = new ZipEntry(fileEntryName);
byte[] buffer = new byte[16];
BigEndianCodec.putLong(buffer, 0, System.currentTimeMillis());
BigEndianCodec.putLong(buffer, 8, annotatedSize);
zipEntry.setExtra(buffer);
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.write(
FileServerTestUtil.createRandomData(actualSize));
zipOutputStream.closeEntry();
}
try {
FileHelperUtil.unzip(
corrupttedZipFilePath, FileHelperUtil.TEMP_DIR_PATH);
Assert.fail();
}
catch (IOException ioe) {
Assert.assertEquals(
"Zip stream for entry " + fileEntryName + " is " + actualSize +
" bytes but should " + annotatedSize + " bytes",
ioe.getMessage());
}
}
@Test
public void testUnzipImpossibleScenario() throws IOException {
FileSystem fileSystem = FileSystems.getDefault();
FileSystemProvider fileSystemProvider =
new FileSystemProviderWrapper(fileSystem.provider()) {
@Override
public InputStream newInputStream(
Path path, OpenOption... options) {
return null;
}
};
Path impossiableSourceFilePath = Paths.get("ImpossibleSourceFilePath");
impossiableSourceFilePath = fileSystemProvider.getPath(
impossiableSourceFilePath.toUri());
try {
FileHelperUtil.unzip(
impossiableSourceFilePath, FileHelperUtil.TEMP_DIR_PATH);
Assert.fail();
}
catch (NullPointerException npe) {
}
}
@Test
public void testUnzipNoSuchFile() {
try {
FileHelperUtil.unzip(
Paths.get("NoSuchFile"), FileHelperUtil.TEMP_DIR_PATH);
}
catch (IOException ioe) {
Assert.assertSame(NoSuchFileException.class, ioe.getClass());
}
}
@Test
public void testUnzipNullZipInputStream() throws IOException {
try {
FileHelperUtil.unzip((ZipInputStream)null, null);
Assert.fail();
}
catch (NullPointerException npe) {
}
}
@Test
public void testUnzipUnreadableInputStream() {
final IOException ioException = new IOException();
try {
FileHelperUtil.unzip(
new ZipInputStream(
new UnsyncByteArrayInputStream(new byte[0])) {
@Override
public int read() throws IOException {
throw ioException;
}
},
null);
}
catch (IOException ioe) {
Assert.assertSame(ioException, ioe);
}
}
@Test
public void testZipAndUnzip() throws IOException {
// With log
String folderName = "TestFolder";
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
FileHelperUtil.class.getName(), Level.FINEST)) {
Path folderPath = FileServerTestUtil.createFolderWithFiles(
Paths.get(folderName));
Path zipFilePath = FileServerTestUtil.registerForCleanUp(
FileHelperUtil.zip(
folderPath, FileHelperUtil.TEMP_DIR_PATH,
CompressionLevel.BEST_COMPRESSION));
List<LogRecord> logRecords = captureHandler.getLogRecords();
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
LogRecord logRecord = logRecords.remove(0);
String message = logRecord.getMessage();
Assert.assertTrue(message.startsWith("Zipped"));
try (FileSystem fileSystem = FileSystems.newFileSystem(
zipFilePath, null)) {
Files.createDirectory(fileSystem.getPath("EmptyFolder"));
FileServerTestUtil.assertFileEquals(
folderPath, fileSystem.getPath("/"));
}
Path unzipFolderPath = FileServerTestUtil.registerForCleanUp(
FileHelperUtil.unzip(
zipFilePath, FileHelperUtil.TEMP_DIR_PATH));
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
logRecord = logRecords.remove(0);
message = logRecord.getMessage();
Assert.assertTrue(message.startsWith("Unzipped"));
FileServerTestUtil.assertFileEquals(folderPath, unzipFolderPath);
}
// Without log
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
FileHelperUtil.class.getName(), Level.OFF)) {
Path folderPath = FileServerTestUtil.createFolderWithFiles(
Paths.get(folderName));
Path zipFilePath = FileServerTestUtil.registerForCleanUp(
FileHelperUtil.zip(
folderPath, FileHelperUtil.TEMP_DIR_PATH,
CompressionLevel.BEST_COMPRESSION));
List<LogRecord> logRecords = captureHandler.getLogRecords();
Assert.assertTrue(logRecords.isEmpty());
try (FileSystem fileSystem = FileSystems.newFileSystem(
zipFilePath, null)) {
Files.createDirectory(fileSystem.getPath("EmptyFolder"));
FileServerTestUtil.assertFileEquals(
folderPath, fileSystem.getPath("/"));
}
Path unzipFolderPath = FileServerTestUtil.registerForCleanUp(
FileHelperUtil.unzip(
zipFilePath, FileHelperUtil.TEMP_DIR_PATH));
Assert.assertTrue(logRecords.isEmpty());
FileServerTestUtil.assertFileEquals(folderPath, unzipFolderPath);
}
}
@Test
public void testZipImpossibleScenario() throws IOException {
FileSystem fileSystem = FileSystems.getDefault();
FileSystemProvider fileSystemProvider =
new FileSystemProviderWrapper(fileSystem.provider()) {
@Override
public OutputStream newOutputStream(
Path path, OpenOption... options) {
return null;
}
};
Path impossiableDestDirPath = new PathWrapper(
FileHelperUtil.TEMP_DIR_PATH,
new FileSystemWrapper(fileSystem, fileSystemProvider));
try {
FileHelperUtil.zip(
null, impossiableDestDirPath, CompressionLevel.NO_COMPRESSION);
Assert.fail();
}
catch (NullPointerException npe) {
}
}
@Test
public void testZipNoSuchFile() {
try {
FileHelperUtil.zip(
Paths.get("NoSuchFile"), FileHelperUtil.TEMP_DIR_PATH,
CompressionLevel.NO_COMPRESSION);
}
catch (IOException ioe) {
Assert.assertSame(NoSuchFileException.class, ioe.getClass());
}
}
@Test
public void testZipNullZipOutputStream() throws IOException {
try {
FileHelperUtil.zip(
FileServerTestUtil.createFolderWithFiles(
Paths.get("TestFolder")),
(ZipOutputStream)null);
Assert.fail();
}
catch (NullPointerException npe) {
}
}
protected void createFile(Path path) {
try {
boolean deleteDirectory = false;
Path parentPath = path.getParent();
if ((parentPath != null) && Files.notExists(parentPath)) {
Files.createDirectories(parentPath);
deleteDirectory = true;
}
Files.createFile(path);
File file = path.toFile();
file.deleteOnExit();
if (deleteDirectory) {
file = parentPath.toFile();
file.deleteOnExit();
}
}
catch (IOException ioe) {
ReflectionUtil.throwException(ioe);
}
}
}