From 6bda019d37816bfbf187e991dbef7d54e11b0851 Mon Sep 17 00:00:00 2001 From: DManstrator Date: Sat, 13 Oct 2018 23:00:06 +0200 Subject: [PATCH] Initial Commit with an already fully working Version --- .classpath | 19 ++ .gitignore | 3 + .project | 17 ++ .settings/org.eclipse.jdt.core.prefs | 11 + README.md | 20 ++ img/icon-taskbar.png | Bin 0 -> 4858 bytes img/menu_done.png | Bin 0 -> 20751 bytes img/menu_ready.png | Bin 0 -> 16399 bytes img/menu_start.png | Bin 0 -> 15640 bytes .../tk/dmanstrator/filehasher/Hasher.java | 175 +++++++++++++++ .../java/tk/dmanstrator/filehasher/Main.java | 210 ++++++++++++++++++ .../dmanstrator/filehasher/package-info.java | 7 + .../tk/dmanstrator/filehasher/HasherTest.java | 140 ++++++++++++ .../resources/folder1/Another-Testfile.txt | 1 + src/test/resources/folder1/Testfile.txt | 1 + src/test/resources/folder2/Test File.txt | 1 + .../Testfile with \303\234ml\303\244uts.txt" | 1 + 17 files changed, 606 insertions(+) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 README.md create mode 100644 img/icon-taskbar.png create mode 100644 img/menu_done.png create mode 100644 img/menu_ready.png create mode 100644 img/menu_start.png create mode 100644 src/main/java/tk/dmanstrator/filehasher/Hasher.java create mode 100644 src/main/java/tk/dmanstrator/filehasher/Main.java create mode 100644 src/main/java/tk/dmanstrator/filehasher/package-info.java create mode 100644 src/test/java/tk/dmanstrator/filehasher/HasherTest.java create mode 100644 src/test/resources/folder1/Another-Testfile.txt create mode 100644 src/test/resources/folder1/Testfile.txt create mode 100644 src/test/resources/folder2/Test File.txt create mode 100644 "src/test/resources/folder2/Testfile with \303\234ml\303\244uts.txt" diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..5e7e021 --- /dev/null +++ b/.classpath @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4584532 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/lib/ +/src/main/resources/* \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..fd458f2 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + FileHasher + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/README.md b/README.md new file mode 100644 index 0000000..eaef085 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# File-Hasher +A Program for mass-checking the SHA-512 Hash of whole Subdirectories. + +## How to use +Go to the [Releases-Tab](../../releases) and download the attached JAR-File. Put the file into a folder wherever you want but **don't let them in the Downloads Folder** since all Output Files will be created in that folder. + +## Pictures +Here some pictures that you know how the GUI looks like: + +### Program +![Menu on Start](img/menu_start.png)
+![Ready to Go](img/menu_ready.png)
+![Request Done](img/menu_done.png) + +### Icon +![Icon in the Task-Bar](img/icon-taskbar.png) + +## FAQ +- Q: How to use it?
+ A: Read the text above. ;) \ No newline at end of file diff --git a/img/icon-taskbar.png b/img/icon-taskbar.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0be87ebbdb97f68d9a31541af607f7c7c90f5a GIT binary patch literal 4858 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 literal 0 HcmV?d00001 diff --git a/img/menu_done.png b/img/menu_done.png new file mode 100644 index 0000000000000000000000000000000000000000..6ac77fb8d303a1fbe6947b0ef1daf0c8f59efec1 GIT binary patch literal 20751 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 Gu>S{}3IQ$v literal 0 HcmV?d00001 diff --git a/img/menu_ready.png b/img/menu_ready.png new file mode 100644 index 0000000000000000000000000000000000000000..d3e61b5e7994134349be710bb3f80b6831ffa5da GIT binary patch literal 16399 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 literal 0 HcmV?d00001 diff --git a/img/menu_start.png b/img/menu_start.png new file mode 100644 index 0000000000000000000000000000000000000000..44e78807838575bbd958797e05398018e82f3b0d GIT binary patch literal 15640 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 ArT_o{ literal 0 HcmV?d00001 diff --git a/src/main/java/tk/dmanstrator/filehasher/Hasher.java b/src/main/java/tk/dmanstrator/filehasher/Hasher.java new file mode 100644 index 0000000..55410eb --- /dev/null +++ b/src/main/java/tk/dmanstrator/filehasher/Hasher.java @@ -0,0 +1,175 @@ +package tk.dmanstrator.filehasher; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.io.FileUtils; + +/** + * Generates SHA-512 Hashes. Works for all files of a given Folder or for a given File. + * + * @author DManstrator + * + */ +public class Hasher { + + /** + * Encoding for the File in case a filename or the Directory has Umlauts. + */ + private static final String ENCODING = "UTF-8"; + + /** + * Hashing Method. Creates an Output File and returns the Path to it. + * + * @param mainFolderName + * Folder to hash + * @throws IllegalArgumentException + * When the Path is not a folder + * @throws FileNotFoundException + * When the Output File couldn't be created + * @throws UnsupportedEncodingException + * When the Encoding for the Output File is Unsupported + */ + public String hash(String mainFolderName) throws IllegalArgumentException, FileNotFoundException, UnsupportedEncodingException { + File mainPath = new File(mainFolderName); + if (!mainPath.isDirectory()) { + throw new IllegalArgumentException(String.format("Given Folder '%s' is not a folder, re-check that!", mainFolderName)); + } + + Map filesWithHashes = getHashesOfFiles(mainFolderName); + + String tmpFolderName = getFolderName(mainFolderName); // tmp for using folderName in Stream#map + String folderName = tmpFolderName != null ? tmpFolderName : "null"; + + StringBuilder builder = new StringBuilder("Path to scan: " + mainFolderName + System.lineSeparator()); + String hashes = filesWithHashes.entrySet().stream() + .map(entry -> String.format("%s: %s", entry.getKey().getPath().substring(mainFolderName.length() - folderName.length()), + entry.getValue())) + .collect(Collectors.joining(System.lineSeparator())); + + String output = builder.append(hashes).toString(); + + String dateTime = getDateTime(); + File outputFile = new File(String.format("%s-Hashes_%s.txt", folderName, dateTime)); + try { + PrintWriter writer = new PrintWriter(outputFile.getName(), ENCODING); + writer.write(output); + writer.flush(); + writer.close(); + } catch (FileNotFoundException e) { + throw new FileNotFoundException(String.format("An error occured while creating the output file '%s', " + + "make sure the program has the rights to do so!", outputFile.getAbsolutePath())); + } catch (UnsupportedEncodingException e) { + throw new UnsupportedEncodingException(String.format("An error occured because '%s' is an Unsupported Encoding!", ENCODING)); + } + return outputFile.getAbsolutePath(); + } + + /** + * Computes a SHA-512 Hash for every file of the given folder path. + * + * @param mainFolderName + * Path to the starting Directory + * @return a Map with the File as the Key and the SHA-512 Hash as the Value + */ + public Map getHashesOfFiles(String mainFolderName) { + return getHashesOfFiles(new File(mainFolderName)); + } + + /** + * Computes a SHA-512 Hash for every file of the given folder path. + * + * @param mainFolder + * Path as File to the starting Directory + * @return a Map with the File as the Key and the SHA-512 Hash as the Value + */ + public Map getHashesOfFiles(File mainFolder) { + if (!mainFolder.isDirectory()) { + return new HashMap<>(); + } + Collection listFiles = FileUtils.listFiles(mainFolder, null, true); + return listFiles.stream().collect(Collectors.toMap(p -> p, p -> getHashOfFile(p))); + } + + /** + * Generates a SHA-512 Hash of a File. + * + * @param filename + * Name of the File to get the Hash from + * @return a SHA-512 Hash of given File or null if something + * went wrong + */ + public String getHashOfFile(String filename) { + return getHashOfFile(new File(filename)); + } + + /** + * Generates a SHA-512 Hash of a File. + * + * @param file + * File to get the Hash from + * @return a SHA-512 Hash of given File or null if something + * went wrong + * @see https://stackoverflow.com/a/33085670 + */ + public String getHashOfFile(File file) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-512"); + byte[] digest = md.digest(Files.readAllBytes(Paths.get(file.getAbsolutePath()))); + StringBuilder sb = new StringBuilder(); + for(int i=0; i< digest.length; i++){ + sb.append(Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1)); + } + return sb.toString(); + } catch (NoSuchAlgorithmException | IOException e) { + return null; + } + } + + /** + * Gets the deepest Folder Name from a given Path. + * + * @param mainFolderName + * Main Folder Name + * @return the deepest Folder Name from a given Path or null if + * no folder could be retrieved from the given Folder Name. + */ + private String getFolderName(String mainFolderName) { + int lastIndex = mainFolderName.lastIndexOf('/'); + if (lastIndex == -1) { // Maybe Windows + int lastIndexWindows = mainFolderName.lastIndexOf('\\'); + if (lastIndexWindows != -1) { + lastIndex = lastIndexWindows; + } else { + return null; // No slashes at all + } + } + return mainFolderName.substring(lastIndex + 1, mainFolderName.length()); + } + + /** + * Gets the current Time in Format yyyy-MM-dd_HH-mm-ss. + * + * @return the current Time as a readable String + */ + private String getDateTime() { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(System.currentTimeMillis()); + return new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(cal.getTime()).toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/tk/dmanstrator/filehasher/Main.java b/src/main/java/tk/dmanstrator/filehasher/Main.java new file mode 100644 index 0000000..d349565 --- /dev/null +++ b/src/main/java/tk/dmanstrator/filehasher/Main.java @@ -0,0 +1,210 @@ +package tk.dmanstrator.filehasher; + +import java.awt.Desktop; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import javafx.application.Application; +import javafx.geometry.Insets; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.stage.DirectoryChooser; +import javafx.stage.Stage; + +/** + * Main File with GUI Support to execute the File Hasher. + * + * @author DManstrator + * + */ +public class Main extends Application { + + /** + * Starts the GUI. + */ + @Override + public void start(Stage primaryStage) throws Exception { + final DirectoryChooser directoryChooser = new DirectoryChooser(); + configuringDirectoryChooser(directoryChooser); + + // Creating a GridPane container + GridPane grid = new GridPane(); + grid.setVgap(5); + grid.setHgap(5); + + final Label pathLabel = new Label(); + final Label pathNameLabel = new Label("Path:"); + pathNameLabel.setMinWidth(30); + + GridPane.setConstraints(pathNameLabel, 0, 0); + GridPane.setConstraints(pathLabel, 1, 0); + + grid.getChildren().addAll(pathNameLabel, pathLabel); + + Button chooseButton = new Button("Choose A Path"); + Button okayButton = new Button("Okay"); + Button closeButton = new Button("Exit"); + + chooseButton.setOnAction(action -> { + File dir = directoryChooser.showDialog(primaryStage); + if (dir != null) { + pathLabel.setText(dir.getAbsolutePath()); + okayButton.setDisable(false); + } else { + pathLabel.setText(null); + okayButton.setDisable(true); + } + }); + + okayButton.setDisable(true); + + closeButton.setOnAction(action -> { + primaryStage.close(); + }); + + HBox buttons = new HBox(); + buttons.setSpacing(5); + buttons.getChildren().addAll(chooseButton, okayButton, closeButton); + + VBox root = new VBox(); + root.setPadding(new Insets(10)); + root.setSpacing(5); + + root.getChildren().addAll(grid, buttons); + + Scene scene = new Scene(root); + + Image icon = new Image(getClass().getClassLoader().getResourceAsStream("img/logo.png")); + primaryStage.setTitle("File-Hasher"); + primaryStage.setScene(scene); + primaryStage.getIcons().add(icon); + primaryStage.setMinWidth(270); + primaryStage.setMinHeight(115); + primaryStage.show(); + + okayButton.setOnAction(action -> { + Hasher hasher = new Hasher(); + + Button gotoButton = new Button("Open Folder"); + Button returnButton = new Button("Return"); + + String pathToOutputFile = null; + String returnValue; + try { + pathToOutputFile = hasher.hash(pathLabel.getText()); + returnValue = String.format("Successfully created %s as the Output File!", pathToOutputFile); + } catch (IllegalArgumentException | FileNotFoundException | UnsupportedEncodingException e) { + returnValue = e.getMessage(); + gotoButton.setDisable(true); + } + + Label userInformation = new Label(returnValue); + userInformation.setWrapText(true); + + final String outputPath = pathToOutputFile; + gotoButton.setOnAction(gotoAction -> { + String error = openFolderWithFile(outputPath); + if (error != null) { + userInformation.setText(userInformation.getText() + System.lineSeparator() + error); + gotoButton.setDisable(true); + } + }); + + returnButton.setOnAction(returnAction -> { + buttons.getChildren().add(closeButton); // Disappears if not re-added + pathLabel.setText(null); + okayButton.setDisable(true); + primaryStage.setScene(scene); + }); + + HBox newButtons = new HBox(); + newButtons.setSpacing(5); + newButtons.getChildren().addAll(gotoButton, returnButton, closeButton); + + VBox out = new VBox(); + out.setPadding(new Insets(10)); + out.setSpacing(5); + out.getChildren().addAll(userInformation, newButtons); + out.setMinSize(500, 250); + + Scene infoScreen = new Scene(out); + primaryStage.setScene(infoScreen); + primaryStage.setHeight(100); // trick to use min width + + System.out.println(returnValue); + }); + } + + /** + * Opens the Folder to the given File. + * + * @param outputFile + * File to get Folder from + * @return null if the call was successfully, else an Error Response + */ + private static String openFolderWithFile(String outputFile) { + File testFile = new File(outputFile); + if (!testFile.isFile()) { + return "The file isn't avaliable (anymore)!"; + } + try { + // new ProcessBuilder("explorer.exe", "/select,\"" + outputPath + "\"").start(); // doesn't work + Runtime.getRuntime().exec("explorer.exe /select," + outputFile); + } catch (IOException e) { + String currentDir = System.getProperty("user.dir"); + try { + Desktop.getDesktop().open(new File(currentDir)); + } catch (IOException e1) { + return "An Error occured: Cannot open the Folder for you!"; + } + } + + return null; + } + + /** + * Configures a Title and an initial Directory for a given Directory Chooser. + * + * @param directoryChooser + * Directory Chooser to change things on + */ + private void configuringDirectoryChooser(DirectoryChooser directoryChooser) { + directoryChooser.setTitle("Choose a Directory to Scan"); + directoryChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + } + + /** + * Main Method. Checks for Program Arguments, if none given the GUI will be started. + * + * @param args + * Program Arguments + */ + public static void main(String[] args) { + if (args.length != 0) { + Hasher hasher = new Hasher(); + try { + String pathToOutputFile = hasher.hash(args[0]); + String output = String.format("Successfully created %s as the Output File!", pathToOutputFile); + System.out.println(output); + String error = openFolderWithFile(pathToOutputFile); + if (error != null) { + System.err.println(error); + } + System.exit(0); + } catch (IllegalArgumentException | FileNotFoundException | UnsupportedEncodingException e) { + System.err.println(e.getMessage()); + System.exit(-1); + } + } + + Application.launch(args); + } + +} \ No newline at end of file diff --git a/src/main/java/tk/dmanstrator/filehasher/package-info.java b/src/main/java/tk/dmanstrator/filehasher/package-info.java new file mode 100644 index 0000000..cfc3c99 --- /dev/null +++ b/src/main/java/tk/dmanstrator/filehasher/package-info.java @@ -0,0 +1,7 @@ +/** + * Main Package. + * + * @author DManstrator + * + */ +package tk.dmanstrator.filehasher; \ No newline at end of file diff --git a/src/test/java/tk/dmanstrator/filehasher/HasherTest.java b/src/test/java/tk/dmanstrator/filehasher/HasherTest.java new file mode 100644 index 0000000..075ff8c --- /dev/null +++ b/src/test/java/tk/dmanstrator/filehasher/HasherTest.java @@ -0,0 +1,140 @@ +package tk.dmanstrator.filehasher; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +import tk.dmanstrator.filehasher.Hasher; + +/** + * Test Class for testing the Hasher. Used the following websites to compare the Hashes:
    + * https://emn178.github.io/online-tools/sha512_file_hash.html
    + * https://hash.online-convert.com/sha512-generator + * + * @author DManstrator + * + */ +public class HasherTest { + + /** + * Map with Filepaths and SHA-512 Hashes to compare. + */ + private final static Map HASHES = new HashMap() { + private static final long serialVersionUID = 5636217836120806223L; + { + put("src\\test\\resources\\folder1\\Testfile.txt", + "861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8"); + put("src\\test\\resources\\folder1\\Another-Testfile.txt", + "f11a72665f07e477918c6c8cef29f30942ea7d4ac7dd15bb61b0f6d52e284c4b31978d3238878994a30b7ddcf22cb4ffdad82cb407ecd418a92d0ce78c1a49dc"); + put("src\\test\\resources\\folder2\\Test File.txt", + "861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8"); + put("src\\test\\resources\\folder2\\Testfile with Ümläuts.txt", + "c966988e7254a05ae05bb921a2955899dc47c209120b26faf96a6701cd884b258a3eb6bd5c035ed4e3e3865a84719332a05c458dad2da655a46ca29fa6d2dbc9"); + } + }; + + /** + * Tests if the IllegalArgumentException is successfully thrown. + * + * @throws IllegalArgumentException + * @throws FileNotFoundException + * @throws UnsupportedEncodingException + */ + @Test(expected = IllegalArgumentException.class) + public void testHashingFoldersNotValidPath() throws IllegalArgumentException, FileNotFoundException, UnsupportedEncodingException { + Hasher hasher = new Hasher(); + hasher.hash("not/valid/path"); + } + + /** + * Tests if the program works normally and creates an output file. Also used "Windows Slashes" (Backslashes). + * + * @throws IllegalArgumentException + * @throws FileNotFoundException + * @throws UnsupportedEncodingException + */ + @Test + public void testHashingFoldersRun() throws IllegalArgumentException, FileNotFoundException, UnsupportedEncodingException { + Hasher hasher = new Hasher(); + String hash = hasher.hash("src\\test\\resources\\folder1"); + Assert.assertNotNull(hash); + } + + /** + * Tests if the SHA-512 Hashes are successfully computed. + */ + @Test + public void testHashingFoldersNormal() { + Hasher hasher = new Hasher(); + Map hashesOfFiles = hasher.getHashesOfFiles("src/test/resources/folder1"); + for (Map.Entry entry : hashesOfFiles.entrySet()) { + boolean found = false; + for (Map.Entry hashEntry : HASHES.entrySet()) { + if (entry.getKey().equals(new File(hashEntry.getKey()))) { + found = true; + Assert.assertEquals(entry.getValue(), hashEntry.getValue()); + break; + } + } + Assert.assertTrue(found); + } + } + + /** + * Tests if the SHA-512 Hashes are successfully computed even there are Umlauts in the File Names. + */ + @Test + public void testHashingFoldersAdvanced() { + Hasher hasher = new Hasher(); + Map hashesOfFiles = hasher.getHashesOfFiles("src/test/resources/folder2"); + for (Map.Entry entry : hashesOfFiles.entrySet()) { + boolean found = false; + for (Map.Entry hashEntry : HASHES.entrySet()) { + if (entry.getKey().equals(new File(hashEntry.getKey()))) { + found = true; + Assert.assertEquals(entry.getValue(), hashEntry.getValue()); + break; + } + } + Assert.assertTrue(found); + } + } + + /** + * Checks if the hash of a normal file gets successfully computed. + */ + @Test + public void testHashingFileNormal() { + Hasher hasher = new Hasher(); + String hashOfFile = hasher.getHashOfFile("src/test/resources/folder1/Testfile.txt"); + Assert.assertEquals("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", + hashOfFile); + } + + /** + * Checks if the hash of a file with Umlauts in the name gets successfully computed. + */ + @Test + public void testHashingFileAdvanced() { + Hasher hasher = new Hasher(); + String hashOfFile = hasher.getHashOfFile("src/test/resources/folder2/Testfile with Ümläuts.txt"); + Assert.assertEquals("c966988e7254a05ae05bb921a2955899dc47c209120b26faf96a6701cd884b258a3eb6bd5c035ed4e3e3865a84719332a05c458dad2da655a46ca29fa6d2dbc9", + hashOfFile); + } + + /** + * Checks if an invalid folder path returns an empty map. + */ + @Test + public void testEmptyMap() { + Hasher hasher = new Hasher(); + Map hashesOfFiles = hasher.getHashesOfFiles("not/valid/path"); + Assert.assertTrue(hashesOfFiles.isEmpty()); + } + +} \ No newline at end of file diff --git a/src/test/resources/folder1/Another-Testfile.txt b/src/test/resources/folder1/Another-Testfile.txt new file mode 100644 index 0000000..32f411c --- /dev/null +++ b/src/test/resources/folder1/Another-Testfile.txt @@ -0,0 +1 @@ +Another one! \ No newline at end of file diff --git a/src/test/resources/folder1/Testfile.txt b/src/test/resources/folder1/Testfile.txt new file mode 100644 index 0000000..c57eff5 --- /dev/null +++ b/src/test/resources/folder1/Testfile.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/src/test/resources/folder2/Test File.txt b/src/test/resources/folder2/Test File.txt new file mode 100644 index 0000000..c57eff5 --- /dev/null +++ b/src/test/resources/folder2/Test File.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git "a/src/test/resources/folder2/Testfile with \303\234ml\303\244uts.txt" "b/src/test/resources/folder2/Testfile with \303\234ml\303\244uts.txt" new file mode 100644 index 0000000..8f80d0c --- /dev/null +++ "b/src/test/resources/folder2/Testfile with \303\234ml\303\244uts.txt" @@ -0,0 +1 @@ +Just in case.. \ No newline at end of file