/* * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.spi.FileSystemProvider; import java.util.Map; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; /* @test * @bug 8202285 * @build Mismatch * @run testng Mismatch * @summary Unit test for the mismatch method */ @Test(groups = "mismatch") public class Mismatch { // the standard buffer size final static int BUFFER_SIZE = 8192; private static final int MISMATCH_NO = -1; // Map to be used for creating a ZIP archive private static final Map ZIPFS_MAP = Map.of("create", "true"); // temporary test directory where all test files will be created Path testDir; Path test0a; // empty file, size = 0 Path test0b; // empty file, size = 0 /** * The following files are used to test cases where the files are smaller, * equal and greater than the buffer size of 8192. */ Path test1024a; // a test file with size 1024 Path test1024b; // a test file with size 1024, identical to test1024a Path test1024m1014a; // a test file with size 1024, with an altered byte at 1014 Path test1024m1014b; // a test file with size 1024, with an altered byte at 1014 Path test1024m60; // a test file with size 1024, with an altered byte at 60 Path test8192a; // a test file with size 8192 Path test8192b; // a test file with size 8192, identical to test8192a Path test8192m8182a; // a test file with size 8192, with an altered byte at 8182 Path test8192m8182b; // a test file with size 8192, with an altered byte at 8182 Path test8192m60; // a test file with size 8192, with an altered byte at 60 /** * Files with a size >= BUFFER_SIZE to verify the situation where they will be * read a few times before a mismatch is found in the last full or partial buffer */ Path test65536a; // a test file with size 65536 Path test65536b; // a test file with size 65536, identical to test65536a Path test65536m65526a; // a test file with size 65536, with an altered byte at 65526 Path test65536m65526b; // a test file with size 65536, with an altered byte at 65526 Path test65536m60; // a test file with size 65536, with an altered byte at 60 Path test69632a; // a test file with size 69632 Path test69632b; // a test file with size 69632, identical to test69632a Path test69632m69622a; // a test file with size 69632, with an altered byte at 69622 Path test69632m69622b; // a test file with size 69632, with an altered byte at 69622 Path test69632m60; // a test file with size 69632, with an altered byte at 60 /** * Larger files (>=1048576) to exercise the process */ Path test1048576a; // a test file with size 1048576 Path test1048576b; // a test file with size 1048576, identical to test1048576a Path test1048576m1048566a; // a test file with size 1048576, with an altered byte at 1048566 Path test1048576m1048566b; // a test file with size 1048576, with an altered byte at 1048566 Path test1048576m60; // a test file with size 1048576, with an altered byte at 60 Path test1052672m1052662a; // a test file with size 1052672, with an altered byte at 1052662 Path test1052672m1052662b; // a test file with size 1052672, with an altered byte at 1052662 Path test1052672m60; // a test file with size 1052672, with an altered byte at 60 @BeforeClass void setup() throws IOException { /** * Create files with alphanumeric contents in a temporary directory */ testDir = Files.createTempDirectory("testMismatch"); // create empty files int size = 0; test0a = createANFile(testDir, "test0a", 0, -1, (byte)' '); test0b = createANFile(testDir, "test0b", 0, -1, (byte)' '); // The Impl uses a standard buffer of 8192 // Test cases with files <= and > 8192 size = 1024; test1024a = createANFile(testDir, "test1024a", size, -1, (byte)' '); test1024b = createANFile(testDir, "test1024b", size, -1, (byte)' '); test1024m1014a = createANFile(testDir, "test1024m1014a", size, size - 10, (byte)'@'); test1024m1014b = createANFile(testDir, "test1024m1014b", size, size - 10, (byte)'#'); test1024m60 = createANFile(testDir, "test1024m60", size, 60, (byte)'$'); size = BUFFER_SIZE; test8192a = createANFile(testDir, "test8192a", size, -1, (byte)' '); test8192b = createANFile(testDir, "test8192b", size, -1, (byte)' '); test8192m8182a = createANFile(testDir, "test8192m8182a", size, size - 10, (byte)'@'); test8192m8182b = createANFile(testDir, "test8192m8182b", size, size - 10, (byte)'#'); test8192m60 = createANFile(testDir, "test8192m60", size, 60, (byte)'$'); // create files with size several times > BUFFER_SIZE to be used for tests that verify // the situations where they are read into full buffers a few times size = BUFFER_SIZE << 3; test65536a = createANFile(testDir, "test65536a", size, -1, (byte)' '); test65536b = createANFile(testDir, "test65536b", size, -1, (byte)' '); test65536m65526a = createANFile(testDir, "test65536m65526a", size, size - 10, (byte)'@'); test65536m65526b = createANFile(testDir, "test65536m65526b", size, size - 10, (byte)'#'); test65536m60 = createANFile(testDir, "test65536m60", size, 60, (byte)'$'); // create files with sizes that will be iterated several times with full buffers, and // then a partial one at the last size = 69632; test69632a = createANFile(testDir, "test69632a", size, -1, (byte)' '); test69632b = createANFile(testDir, "test69632b", size, -1, (byte)' '); test69632m69622a = createANFile(testDir, "test69632m69622a", size, size - 10, (byte)'@'); test69632m69622b = createANFile(testDir, "test69632m69622b", size, size - 10, (byte)'#'); test69632m60 = createANFile(testDir, "test69632m60", size, 60, (byte)'$'); // create larger files with >= 1048576. The mismatching will be similar. These are just // tests to exercise the process with larger files size = 1048576; test1048576a = createANFile(testDir, "test1048576a", size, -1, (byte)' '); test1048576b = createANFile(testDir, "test1048576b", size, -1, (byte)' '); test1048576m1048566a = createANFile(testDir, "test1048576m1048566a", size, size - 10, (byte)'@'); test1048576m1048566b = createANFile(testDir, "test1048576m1048566b", size, size - 10, (byte)'#'); test1048576m60 = createANFile(testDir, "test1048576m60", size, 60, (byte)'$'); size = 1052672; test1052672m1052662a = createANFile(testDir, "test1052672m1052662a", size, size - 10, (byte)'@'); test1052672m1052662b = createANFile(testDir, "test1052672m1052662b", size, size - 10, (byte)'#'); test1052672m60 = createANFile(testDir, "test1052672m60", size, 60, (byte)'$'); } @AfterClass void cleanup() throws IOException { // clean up files created under the test directory Files.walk(testDir).map(Path::toFile).forEach(File::delete); Files.deleteIfExists(testDir); } /* * DataProvider for mismatch test. Provides the following fields: * path1 -- the path to a file * path2 -- the path to another file * expected -- expected result of the mismatch method * note -- a note about the test */ @DataProvider(name = "testMismatch") public Object[][] getDataForMismatch() throws IOException { Path foo = Paths.get("foo"); return new Object[][]{ // Spec Case 1: the two paths locate the same file , even if one does not exist {foo, foo, MISMATCH_NO, "Same file, no mismatch"}, {test0a, test0a, MISMATCH_NO, "Same file, no mismatch"}, {test1024a, test1024a, MISMATCH_NO, "Same file, no mismatch"}, // Spec Case 2: The two files are the same size, and every byte in the first file // is identical to the corresponding byte in the second file. {test0a, test0b, MISMATCH_NO, "Sizes == 0, no mismatch"}, {test1024a, test1024b, MISMATCH_NO, "size = 1024 < buffer = 8192, no mismatch"}, {test8192a, test8192b, MISMATCH_NO, "size = 8192 = buffer = 8192, no mismatch"}, {test65536a, test65536b, MISMATCH_NO, "read 8 * full buffer, no mismatch"}, {test69632a, test69632b, MISMATCH_NO, "read 8 * full buffer plus a partial buffer, no mismatch"}, // Spec Case 3: the value returned is the position of the first mismatched byte // Impl: the impl uses a buffer 8192. The testcases below covers a range of files // with sizes <= and > the buffer size. The last buffer is either full or partially full. {test1024m1014a, test1024m1014b, 1014, "read one partial buffer, mismatch = 1014"}, {test1024m1014a, test1024m60, 60, "read one partial buffer, mismatch = 60"}, {test1024m1014a, test8192m8182a, 1014, "one partial vs full buffer, mismatch = 1014"}, {test8192m8182a, test8192m8182b, 8182, "read one full buffer, mismatch = 8182"}, {test65536m65526a, test65536m65526b, 65526, "read several times, mismatch in the last full buffer, mismatch = 65526"}, {test65536m60, test65536m65526a, 60, "mismatch in the first buffer, mismatch = 60"}, {test69632m69622a, test69632m69622b, 69622, "mismatch in the last partial buffer, mismatch = 69622"}, {test69632m69622a, test69632m60, 60, "mismatch in the first buffer, mismatch = 60"}, {test1048576m1048566a, test1048576m1048566b, 1048566, "mismatch in the last full buffer, mismatch = 1048566"}, {test1048576m60, test1048576m1048566b, 60, "mismatch in the first buffer, mismatch = 60"}, {test1052672m1052662a, test1052672m1052662b, 1052662, "mismatch in the last partial buffer, mismatch = 1052662"}, {test1052672m1052662a, test1052672m60, 60, "mismatch in the first buffer, mismatch = 60"}, // Spec Case 4: returns the size of the smaller file (in bytes) when the files are // different sizes and every byte of the smaller file is identical to the corresponding // byte of the larger file. // Impl: similar to case 3, covers a range of file sizes {test0a, test1024a, 0, "Size of one of files = 0, mismatch at 0"}, {test1024a, test8192a, 1024, "mismatch is the length of the smaller file: 1024"}, {test1024a, test65536a, 1024, "mismatch is the length of the smaller file: 1024"}, {test8192a, test65536a, 8192, "mismatch is the length of the smaller file: 8192"}, {test69632a, test65536a, 65536, "mismatch is the length of the smaller file: 65536"}, {test1048576a, test69632a, 69632, "mismatch is the length of the smaller file: 69632"}, // Spec Case 5: This method is always reflexive (for Path f , mismatch(f,f) returns -1L) // See tests for Spec Case 1. // Spec Case 6: If the file system and files remain static, then this method is symmetric // (for two Paths f and g, mismatch(f,g) will return the same value as mismatch(g,f)). // The following tests are selected from tests for Spec Case 3 with the order of // file paths switched, the returned values are the same as those for Case 3: {test1024m1014b, test1024m1014a, 1014, "read one partial buffer, mismatch = 1014"}, {test1024m60, test1024m1014a, 60, "read one partial buffer, mismatch = 60"}, {test8192m8182a, test1024m1014a, 1014, "one partial vs full buffer, mismatch = 1014"}, }; } /* * DataProvider for mismatch tests involving ZipFS using a few test cases selected * from those of the original mismatch tests. */ @DataProvider(name = "testMismatchZipfs") public Object[][] getDataForMismatchZipfs() throws IOException { return new Object[][]{ {test1024a, test1024a, MISMATCH_NO, "Compares the file and its copy in zip, no mismatch"}, {test8192m8182a, test8192m8182b, 8182, "Compares a copy of test8192m8182a in zip and test8192m8182b, shall return 8182"}, {test1048576a, test65536a, 65536, "mismatch is the length of the smaller file: 65536"}, }; } /* * DataProvider for verifying null handling. */ @DataProvider(name = "testFileNull") public Object[][] getDataForNull() throws IOException { return new Object[][]{ {(Path)null, (Path)null}, {(Path)null, test1024a}, {test1024a, (Path)null}, }; } /* * DataProvider for verifying how the mismatch method handles the situation * when one or both files do not exist. */ @DataProvider(name = "testFileNotExist") public Object[][] getDataForFileNotExist() throws IOException { return new Object[][]{ {Paths.get("foo"), Paths.get("bar")}, {Paths.get("foo"), test1024a}, {test1024a, Paths.get("bar")}, }; } /** * Tests the mismatch method. Refer to the dataProvider testMismatch for more * details about the cases. * @param path the path to a file * @param path2 the path to another file * @param expected the expected result * @param msg the message about the test * @throws IOException if the test fails */ @Test(dataProvider = "testMismatch", priority = 0) public void testMismatch(Path path, Path path2, long expected, String msg) throws IOException { long result = Files.mismatch(path, path2); Assert.assertTrue(result == expected, msg); } /** * Tests the mismatch method by comparing files with those in a ZIP file. * @param path the path to a file * @param path2 the path to another file to be added into a ZIP file * @param expected the expected result * @param msg the message about the test * @throws IOException if the test fails */ @Test(dataProvider = "testMismatchZipfs", priority = 1) public void testMismatchZipfs(Path path, Path path2, long expected, String msg) throws IOException { Path zipPath = Paths.get(testDir.toString(), "TestWithFSZip.zip"); try (FileSystem fs = getZipFSProvider().newFileSystem(zipPath, ZIPFS_MAP)) { Path copy = fs.getPath(path.getFileName().toString()); Files.copy(path, copy, REPLACE_EXISTING); if (path2 == null) { Assert.assertTrue(Files.mismatch(copy, path) == expected, msg); } else { Assert.assertTrue(Files.mismatch(copy, path2) == expected, msg); } } } /** * Verifies that NullPointerException is thrown when one or both files are null. * @param path the path to a file * @param path2 the path to another file * @throws NullPointerException as expected */ @Test(dataProvider = "testFileNull", priority = 2, expectedExceptions = NullPointerException.class) public void testMismatchNull(Path path, Path path2) throws Exception { long result = Files.mismatch(path, path2); } /** * Verifies that IOException is thrown when one or both files do not exist. * @param path the path to a file * @param path2 the path to another file * @throws IOException as expected */ @Test(dataProvider = "testFileNotExist", priority = 2, expectedExceptions = IOException.class) public void testMismatchNotExist(Path path, Path path2) throws IOException { long result = Files.mismatch(path, path2); } /** * Creates a file with alphanumeric content with one character altered * at the specified position. * * @param dir the directory in which the file is to be created * @param purpose the purpose of the file * @param size the size of the file * @param pos the position where the alternative char is to be added. If it * is smaller than zero, no alternation shall be made. * @param c the character * @return path of the created file * @throws IOException */ private static Path createANFile(Path dir, String purpose, int size, int pos, byte c) throws IOException { Path path = Files.createFile(Paths.get(dir.toString(), purpose + ".txt")); if (size > 0) { writeAlphanumericFile(path, size, pos, c); } return path; } private static void writeAlphanumericFile(Path path, int size, int pos, byte c) throws IOException { byte[] a = createAlphanumericArray(size); if (pos > 0) a[pos] = c; Files.write(path, a); } private static byte[] createAlphanumericArray(int length) { byte[] bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 \n".getBytes(); byte[] a = new byte[length]; fillArray(a, bytes); return a; } private static FileSystemProvider getZipFSProvider() { for (FileSystemProvider provider : FileSystemProvider.installedProviders()) { if ("jar".equals(provider.getScheme())) { return provider; } } return null; } /** * Fills the byte array by repeating the bytes from the sample array sequentially * until it is completely filled. * * @param samples the sample array * @return */ private static void fillArray(byte[] a, byte[] samples) { int i1 = 0; for (int i=0; i