Skip to content

Commit

Permalink
Exception is thrown on Windows platform when using FileStorage class …
Browse files Browse the repository at this point in the history
…to create temporary files (#45)

* Create unit tests for FileStorage utility class

* Set protected access to the `isPosix` boolean for the unit tests

* Fix an exception thrown when creating a temporary file on Windows platform using FileStorage API

* Update changelog
  • Loading branch information
besidev authored Oct 3, 2024
1 parent 38b5fe8 commit c22b4b0
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#### Bugfixes
* Fixed the binding of the port in the local server implementation inside the `jpro-core` module to occur only when
necessary, rather than during server creation.
* Fixed exception thrown on Windows platform when using FileStorage class from `jpro-file` module to create temporary
files.

----------------------

Expand Down
45 changes: 38 additions & 7 deletions jpro-file/src/main/java/one/jpro/platform/file/FileStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -23,17 +24,29 @@
*/
public final class FileStorage {

static final FileSystem DEFAULT_FILE_SYSTEM = FileSystems.getDefault();

/**
* Determines if the default file system supports POSIX file attribute views.
*/
private static final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
static final boolean isPosix = DEFAULT_FILE_SYSTEM.supportedFileAttributeViews().contains("posix");

/**
* The directory path to store temporary files. Defaults to a 'tmp' directory within a '.jpro' directory
* in the user's home directory.
*/
public static final Path JPRO_TMP_DIR = Path.of(System.getProperty("user.home"), ".jpro", "tmp");

static {
try {
if (Files.notExists(JPRO_TMP_DIR)) {
Files.createDirectories(JPRO_TMP_DIR);
}
} catch (IOException e) {
throw new ExceptionInInitializerError("Failed to create JPRO temporary directory: " + e.getMessage());
}
}

/**
* Inner class to hold the default POSIX file and directory permissions.
* Permissions are OWNER_READ and OWNER_WRITE.
Expand All @@ -58,11 +71,25 @@ private static Path generatePath(String fileName, String fileType, Path dir) {
int lastIndexOf = fileName.lastIndexOf('.');
if (lastIndexOf > 0) {
s = fileName.substring(0, lastIndexOf) + fileType;
} else if (lastIndexOf == -1) {
s = fileName + fileType;
}

// Create the directories if they don't exist
if (Files.notExists(dir)) {
try {
Files.createDirectories(dir);
} catch (IOException ex) {
throw new IllegalArgumentException("Invalid directory path: " + dir, ex);
}
}

Path name = dir.getFileSystem().getPath(s);
// the generated name should be a simple file name
if (name.getParent() != null)
throw new IllegalArgumentException("Invalid fileName or fileType");
// Ensure the generated name is a simple file name
if (name.getParent() != null) {
throw new IllegalArgumentException("Invalid fileName: " + s + ", it should not contain directory separators.");
}

return dir.resolve(name);
}

Expand All @@ -85,13 +112,17 @@ public static Path createTempFile(@Nullable Path dir,
fileType = Objects.requireNonNullElse(fileType, ".tmp");

FileAttribute<?>[] attrs = null;
if (isPosix && (dir.getFileSystem() == FileSystems.getDefault())) {
if (isPosix && (dir.getFileSystem() == DEFAULT_FILE_SYSTEM)) {
attrs = new FileAttribute<?>[1];
attrs[0] = PosixPermissions.filePermissions;
}

Path path = generatePath(fileName, fileType, dir);
return Files.createFile(path, attrs);
final Path path = generatePath(fileName, fileType, dir);
if (Files.exists(path)) {
return path;
} else {
return (attrs != null) ? Files.createFile(path, attrs) : Files.createFile(path);
}
}

private FileStorage() {
Expand Down
108 changes: 108 additions & 0 deletions jpro-file/src/test/java/one/jpro/platform/file/FileStorageTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package one.jpro.platform.file;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.PosixFilePermissions;

import static org.junit.jupiter.api.Assertions.*;

/**
* Unit tests for the FileStorage utility class.
*
* @author Besmir Beqiri
*/
class FileStorageTest {

@TempDir
static Path tempDir;

/**
* Ensures the JPRO_TMP_DIR exists before any tests are run.
*/
@BeforeAll
static void setup() {
assertTrue(Files.exists(FileStorage.JPRO_TMP_DIR));
}

/**
* Tests that a temporary file can be created with default parameters.
*/
@Test
void testCreateTempFileWithDefaults() throws IOException {
Path tempFile = FileStorage.createTempFile(null, null, null);
assertNotNull(tempFile);
assertTrue(Files.exists(tempFile), "File should exist");
assertTrue(tempFile.getFileName().toString().endsWith(".tmp"), "File should have .tmp extension");
}

/**
* Tests that a temporary file can be created in a custom directory.
*/
@Test
void testCreateTempFileInCustomDir() throws IOException {
Path customDir = tempDir.resolve("custom");
Files.createDirectories(customDir);

Path tempFile = FileStorage.createTempFile(customDir, "testfile", ".test");
assertNotNull(tempFile);
assertTrue(Files.exists(tempFile), "File should exist in custom directory");
assertEquals(customDir, tempFile.getParent(), "File should be created in the custom directory");
assertTrue(tempFile.getFileName().toString().endsWith(".test"), "File should have .test extension");
}

/**
* Tests that a temporary file can be created with POSIX permissions.
* Only runs on POSIX-compliant file systems.
*/
@Test
void testCreateTempFileWithPosixPermissions() throws IOException {
if (FileStorage.isPosix) {
Path posixFile = FileStorage.createTempFile(tempDir, "posixfile", ".posix");

assertTrue(Files.exists(posixFile), "File should exist");
assertTrue(Files.getPosixFilePermissions(posixFile).containsAll(PosixFilePermissions.fromString("rw-------")),
"File should have OWNER_READ and OWNER_WRITE permissions");
}
}

/**
* Tests that a file is created with a different file extension.
*/
@Test
void testCreateTempFileWithDifferentExtension() throws IOException {
Path tempFile = FileStorage.createTempFile(null, "examplefile", ".dat");

assertNotNull(tempFile);
assertTrue(Files.exists(tempFile), "File should exist");
assertTrue(tempFile.getFileName().toString().endsWith(".dat"), "File should have .dat extension");
}

/**
* Tests that generatePath throws an IllegalArgumentException when the file name contains directory separators.
*/
@Test
void testGeneratePathWithInvalidFileName() {
Path customDir = tempDir.resolve("custom");

IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
FileStorage.createTempFile(customDir, "invalid/file/name", ".test");
});

assertTrue(exception.getMessage().contains("Invalid fileName"), "Exception message should indicate invalid file name");
}

/**
* Tests that createTempFile returns the path if the file already exists.
*/
@Test
void testCreateTempFileFileExists() throws IOException {
Path existingFile = FileStorage.createTempFile(tempDir, "existingfile", ".ext");
Path duplicateFile = FileStorage.createTempFile(tempDir, "existingfile", ".ext");

assertEquals(existingFile, duplicateFile, "The method should return the existing file's path");
}
}

0 comments on commit c22b4b0

Please sign in to comment.