|
18 | 18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 | */ |
20 | 20 |
|
21 | | -package com.amaze.filemanager.filesystem.files; |
22 | | - |
23 | | -import static org.junit.Assert.assertArrayEquals; |
24 | | -import static org.junit.Assert.assertEquals; |
25 | | - |
26 | | -import java.io.BufferedInputStream; |
27 | | -import java.io.BufferedOutputStream; |
28 | | -import java.io.File; |
29 | | -import java.io.FileInputStream; |
30 | | -import java.io.FileOutputStream; |
31 | | -import java.io.IOException; |
32 | | -import java.nio.channels.Channels; |
33 | | -import java.security.DigestInputStream; |
34 | | -import java.security.MessageDigest; |
35 | | -import java.security.NoSuchAlgorithmException; |
36 | | - |
37 | | -import org.junit.Before; |
38 | | -import org.junit.Test; |
39 | | -import org.junit.runner.RunWith; |
40 | | - |
41 | | -import com.amaze.filemanager.asynchronous.management.ServiceWatcherUtil; |
42 | | -import com.amaze.filemanager.test.DummyFileGenerator; |
43 | | -import com.amaze.filemanager.utils.ProgressHandler; |
44 | | - |
45 | | -import androidx.test.ext.junit.runners.AndroidJUnit4; |
46 | | -import androidx.test.platform.app.InstrumentationRegistry; |
47 | | - |
48 | | -@RunWith(AndroidJUnit4.class) |
49 | | -public class GenericCopyUtilEspressoTest { |
50 | | - |
51 | | - private GenericCopyUtil copyUtil; |
52 | | - |
53 | | - private File file1, file2; |
54 | | - |
55 | | - @Before |
56 | | - public void setUp() throws IOException { |
57 | | - copyUtil = |
58 | | - new GenericCopyUtil( |
59 | | - InstrumentationRegistry.getInstrumentation().getTargetContext(), new ProgressHandler()); |
60 | | - file1 = File.createTempFile("test", "bin"); |
61 | | - file2 = File.createTempFile("test", "bin"); |
62 | | - file1.deleteOnExit(); |
63 | | - file2.deleteOnExit(); |
64 | | - } |
65 | | - |
66 | | - @Test |
67 | | - public void testCopyFile1() throws IOException, NoSuchAlgorithmException { |
68 | | - doTestCopyFile1(512); |
69 | | - doTestCopyFile1(10 * 1024 * 1024); |
70 | | - } |
71 | | - |
72 | | - @Test |
73 | | - public void testCopyFile2() throws IOException, NoSuchAlgorithmException { |
74 | | - doTestCopyFile2(512); |
75 | | - doTestCopyFile2(10 * 1024 * 1024); |
76 | | - } |
77 | | - |
78 | | - @Test |
79 | | - public void testCopyFile3() throws IOException, NoSuchAlgorithmException { |
80 | | - doTestCopyFile3(512); |
81 | | - doTestCopyFile3(10 * 1024 * 1024); |
82 | | - } |
83 | | - |
84 | | - // doCopy(ReadableByteChannel in, WritableByteChannel out) |
85 | | - private void doTestCopyFile1(int size) throws IOException, NoSuchAlgorithmException { |
86 | | - byte[] checksum = DummyFileGenerator.createFile(file1, size); |
87 | | - copyUtil.doCopy( |
88 | | - new FileInputStream(file1).getChannel(), |
89 | | - Channels.newChannel(new FileOutputStream(file2)), |
90 | | - ServiceWatcherUtil.UPDATE_POSITION); |
91 | | - assertEquals(file1.length(), file2.length()); |
92 | | - assertSha1Equals(checksum, file2); |
93 | | - } |
94 | | - |
95 | | - // copy(FileChannel in, FileChannel out) |
96 | | - private void doTestCopyFile2(int size) throws IOException, NoSuchAlgorithmException { |
97 | | - byte[] checksum = DummyFileGenerator.createFile(file1, size); |
98 | | - copyUtil.copyFile( |
99 | | - new FileInputStream(file1).getChannel(), |
100 | | - new FileOutputStream(file2).getChannel(), |
101 | | - ServiceWatcherUtil.UPDATE_POSITION); |
102 | | - assertEquals(file1.length(), file2.length()); |
103 | | - assertSha1Equals(checksum, file2); |
104 | | - } |
105 | | - |
106 | | - // copy(BufferedInputStream in, BufferedOutputStream out) |
107 | | - private void doTestCopyFile3(int size) throws IOException, NoSuchAlgorithmException { |
108 | | - byte[] checksum = DummyFileGenerator.createFile(file1, size); |
109 | | - copyUtil.copyFile( |
110 | | - new BufferedInputStream(new FileInputStream(file1)), |
111 | | - new BufferedOutputStream(new FileOutputStream(file2)), |
112 | | - ServiceWatcherUtil.UPDATE_POSITION); |
113 | | - assertEquals(file1.length(), file2.length()); |
114 | | - assertSha1Equals(checksum, file2); |
115 | | - } |
116 | | - |
117 | | - private void assertSha1Equals(byte[] expected, File file) |
118 | | - throws NoSuchAlgorithmException, IOException { |
119 | | - MessageDigest md = MessageDigest.getInstance("SHA-1"); |
120 | | - DigestInputStream in = new DigestInputStream(new FileInputStream(file), md); |
121 | | - byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; |
122 | | - while (in.read(buffer) > -1) {} |
123 | | - in.close(); |
124 | | - assertArrayEquals(expected, md.digest()); |
125 | | - } |
| 21 | +package com.amaze.filemanager.filesystem.files |
| 22 | + |
| 23 | +import androidx.test.ext.junit.runners.AndroidJUnit4 |
| 24 | +import androidx.test.platform.app.InstrumentationRegistry |
| 25 | +import com.amaze.filemanager.fileoperations.utils.UpdatePosition |
| 26 | +import com.amaze.filemanager.test.DummyFileGenerator |
| 27 | +import com.amaze.filemanager.utils.ProgressHandler |
| 28 | +import org.junit.Assert.assertArrayEquals |
| 29 | +import org.junit.Assert.assertEquals |
| 30 | +import org.junit.Assert.assertTrue |
| 31 | +import org.junit.Before |
| 32 | +import org.junit.Test |
| 33 | +import org.junit.runner.RunWith |
| 34 | +import java.io.BufferedInputStream |
| 35 | +import java.io.BufferedOutputStream |
| 36 | +import java.io.File |
| 37 | +import java.io.FileInputStream |
| 38 | +import java.io.FileOutputStream |
| 39 | +import java.nio.channels.Channels |
| 40 | +import java.security.DigestInputStream |
| 41 | +import java.security.MessageDigest |
| 42 | + |
| 43 | +/** |
| 44 | + * Instrumented tests for [GenericCopyUtil] to verify the correctness of file copying |
| 45 | + * and progress updates. |
| 46 | + */ |
| 47 | +@RunWith(AndroidJUnit4::class) |
| 48 | +class GenericCopyUtilEspressoTest { |
| 49 | + private lateinit var progressHandler: ProgressHandler |
| 50 | + private lateinit var copyUtil: GenericCopyUtil |
| 51 | + private lateinit var file1: File |
| 52 | + private lateinit var file2: File |
| 53 | + |
| 54 | + /** |
| 55 | + * Pre-test setup. |
| 56 | + */ |
| 57 | + @Before |
| 58 | + fun setUp() { |
| 59 | + progressHandler = ProgressHandler() |
| 60 | + copyUtil = |
| 61 | + GenericCopyUtil( |
| 62 | + InstrumentationRegistry.getInstrumentation().targetContext, |
| 63 | + progressHandler, |
| 64 | + ) |
| 65 | + file1 = File.createTempFile("test", "bin").also { it.deleteOnExit() } |
| 66 | + file2 = File.createTempFile("test", "bin").also { it.deleteOnExit() } |
| 67 | + } |
| 68 | + |
| 69 | + /** |
| 70 | + * Test doCopy with small file |
| 71 | + */ |
| 72 | + @Test |
| 73 | + fun testDoCopySmallFile() { |
| 74 | + verifyDoCopy(512) |
| 75 | + } |
| 76 | + |
| 77 | + /** |
| 78 | + * Test doCopy with large file |
| 79 | + */ |
| 80 | + @Test |
| 81 | + fun testDoCopyLargeFile() { |
| 82 | + verifyDoCopy(10 * 1024 * 1024) |
| 83 | + } |
| 84 | + |
| 85 | + /** |
| 86 | + * Test doCopy with empty file |
| 87 | + */ |
| 88 | + @Test |
| 89 | + fun testDoCopyEmptyFile() { |
| 90 | + verifyDoCopy(0) |
| 91 | + } |
| 92 | + |
| 93 | + private fun verifyDoCopy(size: Int) { |
| 94 | + val checksum = DummyFileGenerator.createFile(file1, size) |
| 95 | + val progressUpdates = mutableListOf<Long>() |
| 96 | + val updatePosition = UpdatePosition { progressUpdates.add(it) } |
| 97 | + copyUtil.doCopy( |
| 98 | + FileInputStream(file1).channel, |
| 99 | + Channels.newChannel(FileOutputStream(file2)), |
| 100 | + updatePosition, |
| 101 | + ) |
| 102 | + assertEquals(file1.length(), file2.length()) |
| 103 | + if (size > 0) { |
| 104 | + assertSha1Equals(checksum, file2) |
| 105 | + } |
| 106 | + assertEquals("Progress sum should equal file size", file1.length(), progressUpdates.sum()) |
| 107 | + } |
| 108 | + |
| 109 | + /** |
| 110 | + * Test copyFile(FileChannel, FileChannel) with small file |
| 111 | + */ |
| 112 | + @Test |
| 113 | + fun testCopyFileChannelSmallFile() { |
| 114 | + verifyCopyFileChannel(512) |
| 115 | + } |
| 116 | + |
| 117 | + /** |
| 118 | + * Test copyFile(FileChannel, FileChannel) with large file |
| 119 | + */ |
| 120 | + @Test |
| 121 | + fun testCopyFileChannelLargeFile() { |
| 122 | + verifyCopyFileChannel(10 * 1024 * 1024) |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * Test copyFile(FileChannel, FileChannel) with empty file |
| 127 | + */ |
| 128 | + @Test |
| 129 | + fun testCopyFileChannelEmptyFile() { |
| 130 | + verifyCopyFileChannel(0) |
| 131 | + } |
| 132 | + |
| 133 | + private fun verifyCopyFileChannel(size: Int) { |
| 134 | + val checksum = DummyFileGenerator.createFile(file1, size) |
| 135 | + val progressUpdates = mutableListOf<Long>() |
| 136 | + val updatePosition = UpdatePosition { progressUpdates.add(it) } |
| 137 | + copyUtil.copyFile( |
| 138 | + FileInputStream(file1).channel, |
| 139 | + FileOutputStream(file2).channel, |
| 140 | + updatePosition, |
| 141 | + ) |
| 142 | + assertEquals(file1.length(), file2.length()) |
| 143 | + if (size > 0) { |
| 144 | + assertSha1Equals(checksum, file2) |
| 145 | + } |
| 146 | + assertEquals("Progress sum should equal file size", file1.length(), progressUpdates.sum()) |
| 147 | + } |
| 148 | + |
| 149 | + /** |
| 150 | + * Test copyFile(BufferedInputStream, BufferedOutputStream) with small file |
| 151 | + */ |
| 152 | + @Test |
| 153 | + fun testCopyBufferedStreamsSmallFile() { |
| 154 | + verifyCopyBufferedStreams(512) |
| 155 | + } |
| 156 | + |
| 157 | + /** |
| 158 | + * Test copyFile(BufferedInputStream, BufferedOutputStream) with large file |
| 159 | + */ |
| 160 | + @Test |
| 161 | + fun testCopyBufferedStreamsLargeFile() { |
| 162 | + verifyCopyBufferedStreams(10 * 1024 * 1024) |
| 163 | + } |
| 164 | + |
| 165 | + /** |
| 166 | + * Test copyFile(BufferedInputStream, BufferedOutputStream) with empty file |
| 167 | + */ |
| 168 | + @Test |
| 169 | + fun testCopyBufferedStreamsEmptyFile() { |
| 170 | + verifyCopyBufferedStreams(0) |
| 171 | + } |
| 172 | + |
| 173 | + private fun verifyCopyBufferedStreams(size: Int) { |
| 174 | + val checksum = DummyFileGenerator.createFile(file1, size) |
| 175 | + val progressUpdates = mutableListOf<Long>() |
| 176 | + val updatePosition = UpdatePosition { progressUpdates.add(it) } |
| 177 | + copyUtil.copyFile( |
| 178 | + BufferedInputStream(FileInputStream(file1)), |
| 179 | + BufferedOutputStream(FileOutputStream(file2)), |
| 180 | + updatePosition, |
| 181 | + ) |
| 182 | + assertEquals(file1.length(), file2.length()) |
| 183 | + if (size > 0) { |
| 184 | + assertSha1Equals(checksum, file2) |
| 185 | + } |
| 186 | + assertEquals("Progress sum should equal file size", file1.length(), progressUpdates.sum()) |
| 187 | + } |
| 188 | + |
| 189 | + /** |
| 190 | + * Test copyFile(FileChannel, BufferedOutputStream) for small files |
| 191 | + */ |
| 192 | + @Test |
| 193 | + fun testCopyFileChannelToBufferedOutputStreamSmallFile() { |
| 194 | + verifyCopyFileChannelToBufferedOutputStream(512) |
| 195 | + } |
| 196 | + |
| 197 | + /** |
| 198 | + * Test copyFile(FileChannel, BufferedOutputStream) for large files |
| 199 | + */ |
| 200 | + @Test |
| 201 | + fun testCopyFileChannelToBufferedOutputStreamLargeFile() { |
| 202 | + verifyCopyFileChannelToBufferedOutputStream(10 * 1024 * 1024) |
| 203 | + } |
| 204 | + |
| 205 | + private fun verifyCopyFileChannelToBufferedOutputStream(size: Int) { |
| 206 | + val checksum = DummyFileGenerator.createFile(file1, size) |
| 207 | + val progressUpdates = mutableListOf<Long>() |
| 208 | + val updatePosition = UpdatePosition { progressUpdates.add(it) } |
| 209 | + copyUtil.copyFile( |
| 210 | + FileInputStream(file1).channel, |
| 211 | + BufferedOutputStream(FileOutputStream(file2)), |
| 212 | + updatePosition, |
| 213 | + ) |
| 214 | + assertEquals(file1.length(), file2.length()) |
| 215 | + if (size > 0) { |
| 216 | + assertSha1Equals(checksum, file2) |
| 217 | + } |
| 218 | + assertEquals("Progress sum should equal file size", file1.length(), progressUpdates.sum()) |
| 219 | + } |
| 220 | + |
| 221 | + /** |
| 222 | + * Test copy cancelled |
| 223 | + */ |
| 224 | + @Test |
| 225 | + fun testCancellation() { |
| 226 | + val size = 10 * 1024 * 1024 |
| 227 | + DummyFileGenerator.createFile(file1, size) |
| 228 | + progressHandler.setCancelled(true) |
| 229 | + val progressUpdates = mutableListOf<Long>() |
| 230 | + val updatePosition = UpdatePosition { progressUpdates.add(it) } |
| 231 | + copyUtil.doCopy( |
| 232 | + FileInputStream(file1).channel, |
| 233 | + Channels.newChannel(FileOutputStream(file2)), |
| 234 | + updatePosition, |
| 235 | + ) |
| 236 | + assertTrue( |
| 237 | + "Cancelled copy should write less than full size", |
| 238 | + file2.length() < file1.length(), |
| 239 | + ) |
| 240 | + } |
| 241 | + |
| 242 | + /** |
| 243 | + * Test when copying a large file using the transferTo path, progress updates are batched |
| 244 | + */ |
| 245 | + @Test |
| 246 | + fun testBatchedProgress_transferToPath() { |
| 247 | + val size = 10 * 1024 * 1024 |
| 248 | + DummyFileGenerator.createFile(file1, size) |
| 249 | + val progressUpdates = mutableListOf<Long>() |
| 250 | + val updatePosition = UpdatePosition { progressUpdates.add(it) } |
| 251 | + copyUtil.copyFile( |
| 252 | + FileInputStream(file1).channel, |
| 253 | + FileOutputStream(file2).channel, |
| 254 | + updatePosition, |
| 255 | + ) |
| 256 | + assertEquals("Progress sum should equal file size", file1.length(), progressUpdates.sum()) |
| 257 | + assertTrue( |
| 258 | + "Batched progress should have fewer callbacks (got ${progressUpdates.size})", |
| 259 | + progressUpdates.size <= 5, |
| 260 | + ) |
| 261 | + } |
| 262 | + |
| 263 | + private fun assertSha1Equals( |
| 264 | + expected: ByteArray, |
| 265 | + file: File, |
| 266 | + ) { |
| 267 | + val md = MessageDigest.getInstance("SHA-1") |
| 268 | + DigestInputStream(FileInputStream(file), md).use { din -> |
| 269 | + val buffer = ByteArray(GenericCopyUtil.DEFAULT_BUFFER_SIZE) |
| 270 | + while (din.read(buffer) > -1) { /* consume */ } |
| 271 | + } |
| 272 | + assertArrayEquals(expected, md.digest()) |
| 273 | + } |
126 | 274 | } |
0 commit comments