|
19 | 19 | package org.apache.hadoop.fs.azurebfs.services; |
20 | 20 |
|
21 | 21 | import java.io.IOException; |
22 | | - |
23 | | -import org.junit.Assert; |
24 | | -import org.junit.Test; |
25 | 22 | import java.util.Arrays; |
| 23 | +import java.util.Optional; |
| 24 | +import java.util.Random; |
| 25 | +import java.util.concurrent.ExecutionException; |
26 | 26 |
|
27 | 27 | import org.assertj.core.api.Assertions; |
| 28 | +import org.junit.Assert; |
| 29 | +import org.junit.Test; |
| 30 | +import org.mockito.Mockito; |
28 | 31 |
|
29 | 32 | import org.apache.hadoop.conf.Configuration; |
30 | 33 | import org.apache.hadoop.fs.FileSystem; |
31 | 34 | import org.apache.hadoop.fs.FSDataInputStream; |
32 | 35 | import org.apache.hadoop.fs.FSDataOutputStream; |
33 | 36 | import org.apache.hadoop.fs.FileStatus; |
| 37 | +import org.apache.hadoop.fs.FutureDataInputStreamBuilder; |
34 | 38 | import org.apache.hadoop.fs.Path; |
35 | 39 | import org.apache.hadoop.fs.azurebfs.AbstractAbfsIntegrationTest; |
36 | 40 | import org.apache.hadoop.fs.azurebfs.AzureBlobFileSystem; |
| 41 | +import org.apache.hadoop.fs.azurebfs.AzureBlobFileSystemStore; |
37 | 42 | import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException; |
38 | 43 | import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TimeoutException; |
39 | 44 | import org.apache.hadoop.fs.azurebfs.contracts.services.ReadBufferStatus; |
40 | 45 | import org.apache.hadoop.fs.azurebfs.utils.TestCachedSASToken; |
41 | 46 | import org.apache.hadoop.fs.azurebfs.utils.TracingContext; |
| 47 | +import org.apache.hadoop.fs.impl.OpenFileParameters; |
42 | 48 |
|
43 | 49 | import static org.mockito.ArgumentMatchers.any; |
| 50 | +import static org.mockito.ArgumentMatchers.anyBoolean; |
| 51 | +import static org.mockito.ArgumentMatchers.anyString; |
44 | 52 | import static org.mockito.Mockito.doReturn; |
45 | 53 | import static org.mockito.Mockito.doThrow; |
46 | 54 | import static org.mockito.Mockito.mock; |
| 55 | +import static org.mockito.Mockito.spy; |
47 | 56 | import static org.mockito.Mockito.times; |
48 | 57 | import static org.mockito.Mockito.verify; |
49 | 58 | import static org.mockito.Mockito.when; |
@@ -192,6 +201,106 @@ public TestAbfsInputStream() throws Exception { |
192 | 201 | ReadBufferManager.getBufferManager().setThresholdAgeMilliseconds(REDUCED_READ_BUFFER_AGE_THRESHOLD); |
193 | 202 | } |
194 | 203 |
|
| 204 | + private void writeBufferToNewFile(Path testFile, byte[] buffer) throws IOException { |
| 205 | + AzureBlobFileSystem fs = getFileSystem(); |
| 206 | + fs.create(testFile); |
| 207 | + FSDataOutputStream out = fs.append(testFile); |
| 208 | + out.write(buffer); |
| 209 | + out.close(); |
| 210 | + } |
| 211 | + |
| 212 | + private void verifyOpenWithProvidedStatus(Path path, FileStatus fileStatus, |
| 213 | + byte[] buf, AbfsRestOperationType source) |
| 214 | + throws IOException, ExecutionException, InterruptedException { |
| 215 | + byte[] readBuf = new byte[buf.length]; |
| 216 | + AzureBlobFileSystem fs = getFileSystem(); |
| 217 | + FutureDataInputStreamBuilder builder = fs.openFile(path); |
| 218 | + builder.withFileStatus(fileStatus); |
| 219 | + FSDataInputStream in = builder.build().get(); |
| 220 | + assertEquals(String.format( |
| 221 | + "Open with fileStatus [from %s result]: Incorrect number of bytes read", |
| 222 | + source), buf.length, in.read(readBuf)); |
| 223 | + assertArrayEquals(String |
| 224 | + .format("Open with fileStatus [from %s result]: Incorrect read data", |
| 225 | + source), readBuf, buf); |
| 226 | + } |
| 227 | + |
| 228 | + private void checkGetPathStatusCalls(Path testFile, FileStatus fileStatus, |
| 229 | + AzureBlobFileSystemStore abfsStore, AbfsClient mockClient, |
| 230 | + AbfsRestOperationType source, TracingContext tracingContext) |
| 231 | + throws IOException { |
| 232 | + |
| 233 | + // verify GetPathStatus not invoked when FileStatus is provided |
| 234 | + abfsStore.openFileForRead(testFile, Optional |
| 235 | + .ofNullable(new OpenFileParameters().withStatus(fileStatus)), null, tracingContext); |
| 236 | + verify(mockClient, times(0).description((String.format( |
| 237 | + "FileStatus [from %s result] provided, GetFileStatus should not be invoked", |
| 238 | + source)))).getPathStatus(anyString(), anyBoolean(), any(TracingContext.class)); |
| 239 | + |
| 240 | + // verify GetPathStatus invoked when FileStatus not provided |
| 241 | + abfsStore.openFileForRead(testFile, |
| 242 | + Optional.empty(), null, |
| 243 | + tracingContext); |
| 244 | + verify(mockClient, times(1).description( |
| 245 | + "GetPathStatus should be invoked when FileStatus not provided")) |
| 246 | + .getPathStatus(anyString(), anyBoolean(), any(TracingContext.class)); |
| 247 | + |
| 248 | + Mockito.reset(mockClient); //clears invocation count for next test case |
| 249 | + } |
| 250 | + |
| 251 | + @Test |
| 252 | + public void testOpenFileWithOptions() throws Exception { |
| 253 | + AzureBlobFileSystem fs = getFileSystem(); |
| 254 | + String testFolder = "/testFolder"; |
| 255 | + Path smallTestFile = new Path(testFolder + "/testFile0"); |
| 256 | + Path largeTestFile = new Path(testFolder + "/testFile1"); |
| 257 | + fs.mkdirs(new Path(testFolder)); |
| 258 | + int readBufferSize = getConfiguration().getReadBufferSize(); |
| 259 | + byte[] smallBuffer = new byte[5]; |
| 260 | + byte[] largeBuffer = new byte[readBufferSize + 5]; |
| 261 | + new Random().nextBytes(smallBuffer); |
| 262 | + new Random().nextBytes(largeBuffer); |
| 263 | + writeBufferToNewFile(smallTestFile, smallBuffer); |
| 264 | + writeBufferToNewFile(largeTestFile, largeBuffer); |
| 265 | + |
| 266 | + FileStatus[] getFileStatusResults = {fs.getFileStatus(smallTestFile), |
| 267 | + fs.getFileStatus(largeTestFile)}; |
| 268 | + FileStatus[] listStatusResults = fs.listStatus(new Path(testFolder)); |
| 269 | + |
| 270 | + // open with fileStatus from GetPathStatus |
| 271 | + verifyOpenWithProvidedStatus(smallTestFile, getFileStatusResults[0], |
| 272 | + smallBuffer, AbfsRestOperationType.GetPathStatus); |
| 273 | + verifyOpenWithProvidedStatus(largeTestFile, getFileStatusResults[1], |
| 274 | + largeBuffer, AbfsRestOperationType.GetPathStatus); |
| 275 | + |
| 276 | + // open with fileStatus from ListStatus |
| 277 | + verifyOpenWithProvidedStatus(smallTestFile, listStatusResults[0], smallBuffer, |
| 278 | + AbfsRestOperationType.ListPaths); |
| 279 | + verifyOpenWithProvidedStatus(largeTestFile, listStatusResults[1], largeBuffer, |
| 280 | + AbfsRestOperationType.ListPaths); |
| 281 | + |
| 282 | + // verify number of GetPathStatus invocations |
| 283 | + AzureBlobFileSystemStore abfsStore = getAbfsStore(fs); |
| 284 | + AbfsClient mockClient = spy(getAbfsClient(abfsStore)); |
| 285 | + setAbfsClient(abfsStore, mockClient); |
| 286 | + TracingContext tracingContext = getTestTracingContext(fs, false); |
| 287 | + checkGetPathStatusCalls(smallTestFile, getFileStatusResults[0], |
| 288 | + abfsStore, mockClient, AbfsRestOperationType.GetPathStatus, tracingContext); |
| 289 | + checkGetPathStatusCalls(largeTestFile, getFileStatusResults[1], |
| 290 | + abfsStore, mockClient, AbfsRestOperationType.GetPathStatus, tracingContext); |
| 291 | + checkGetPathStatusCalls(smallTestFile, listStatusResults[0], |
| 292 | + abfsStore, mockClient, AbfsRestOperationType.ListPaths, tracingContext); |
| 293 | + checkGetPathStatusCalls(largeTestFile, listStatusResults[1], |
| 294 | + abfsStore, mockClient, AbfsRestOperationType.ListPaths, tracingContext); |
| 295 | + |
| 296 | + // Verify with incorrect filestatus |
| 297 | + getFileStatusResults[0].setPath(new Path("wrongPath")); |
| 298 | + intercept(ExecutionException.class, |
| 299 | + () -> verifyOpenWithProvidedStatus(smallTestFile, |
| 300 | + getFileStatusResults[0], smallBuffer, |
| 301 | + AbfsRestOperationType.GetPathStatus)); |
| 302 | + } |
| 303 | + |
195 | 304 | /** |
196 | 305 | * This test expects AbfsInputStream to throw the exception that readAhead |
197 | 306 | * thread received on read. The readAhead thread must be initiated from the |
|
0 commit comments