2121import static org .junit .Assert .assertFalse ;
2222import static org .junit .Assert .assertTrue ;
2323import static org .junit .Assert .fail ;
24+ import static org .junit .Assume .assumeTrue ;
25+ import static org .mockito .ArgumentMatchers .anyString ;
2426import static org .mockito .Mockito .mock ;
2527import static org .mockito .Mockito .verify ;
2628import static org .mockito .Mockito .verifyNoMoreInteractions ;
2729import static org .mockito .Mockito .when ;
2830
2931import java .io .IOException ;
32+ import java .io .InputStream ;
3033import java .nio .ByteBuffer ;
3134
3235import org .apache .hadoop .fs .FSDataInputStream ;
@@ -138,9 +141,11 @@ public void testPositionalReadNoExtra() throws IOException {
138141 ByteBuff bb = new SingleByteBuff (ByteBuffer .wrap (buf , 0 , totalLen ));
139142 FSDataInputStream in = mock (FSDataInputStream .class );
140143 when (in .read (position , buf , bufOffset , totalLen )).thenReturn (totalLen );
144+ when (in .hasCapability (anyString ())).thenReturn (false );
141145 boolean ret = BlockIOUtils .preadWithExtra (bb , in , position , necessaryLen , extraLen );
142146 assertFalse ("Expect false return when no extra bytes requested" , ret );
143147 verify (in ).read (position , buf , bufOffset , totalLen );
148+ verify (in ).hasCapability (anyString ());
144149 verifyNoMoreInteractions (in );
145150 }
146151
@@ -156,10 +161,12 @@ public void testPositionalReadShortReadOfNecessaryBytes() throws IOException {
156161 FSDataInputStream in = mock (FSDataInputStream .class );
157162 when (in .read (position , buf , bufOffset , totalLen )).thenReturn (5 );
158163 when (in .read (5 , buf , 5 , 5 )).thenReturn (5 );
164+ when (in .hasCapability (anyString ())).thenReturn (false );
159165 boolean ret = BlockIOUtils .preadWithExtra (bb , in , position , necessaryLen , extraLen );
160166 assertFalse ("Expect false return when no extra bytes requested" , ret );
161167 verify (in ).read (position , buf , bufOffset , totalLen );
162168 verify (in ).read (5 , buf , 5 , 5 );
169+ verify (in ).hasCapability (anyString ());
163170 verifyNoMoreInteractions (in );
164171 }
165172
@@ -174,9 +181,11 @@ public void testPositionalReadExtraSucceeded() throws IOException {
174181 ByteBuff bb = new SingleByteBuff (ByteBuffer .wrap (buf , 0 , totalLen ));
175182 FSDataInputStream in = mock (FSDataInputStream .class );
176183 when (in .read (position , buf , bufOffset , totalLen )).thenReturn (totalLen );
184+ when (in .hasCapability (anyString ())).thenReturn (false );
177185 boolean ret = BlockIOUtils .preadWithExtra (bb , in , position , necessaryLen , extraLen );
178186 assertTrue ("Expect true return when reading extra bytes succeeds" , ret );
179187 verify (in ).read (position , buf , bufOffset , totalLen );
188+ verify (in ).hasCapability (anyString ());
180189 verifyNoMoreInteractions (in );
181190 }
182191
@@ -191,9 +200,11 @@ public void testPositionalReadExtraFailed() throws IOException {
191200 ByteBuff bb = new SingleByteBuff (ByteBuffer .wrap (buf , 0 , totalLen ));
192201 FSDataInputStream in = mock (FSDataInputStream .class );
193202 when (in .read (position , buf , bufOffset , totalLen )).thenReturn (necessaryLen );
203+ when (in .hasCapability (anyString ())).thenReturn (false );
194204 boolean ret = BlockIOUtils .preadWithExtra (bb , in , position , necessaryLen , extraLen );
195205 assertFalse ("Expect false return when reading extra bytes fails" , ret );
196206 verify (in ).read (position , buf , bufOffset , totalLen );
207+ verify (in ).hasCapability (anyString ());
197208 verifyNoMoreInteractions (in );
198209 }
199210
@@ -210,10 +221,12 @@ public void testPositionalReadShortReadCompletesNecessaryAndExtraBytes()
210221 FSDataInputStream in = mock (FSDataInputStream .class );
211222 when (in .read (position , buf , bufOffset , totalLen )).thenReturn (5 );
212223 when (in .read (5 , buf , 5 , 10 )).thenReturn (10 );
224+ when (in .hasCapability (anyString ())).thenReturn (false );
213225 boolean ret = BlockIOUtils .preadWithExtra (bb , in , position , necessaryLen , extraLen );
214226 assertTrue ("Expect true return when reading extra bytes succeeds" , ret );
215227 verify (in ).read (position , buf , bufOffset , totalLen );
216228 verify (in ).read (5 , buf , 5 , 10 );
229+ verify (in ).hasCapability (anyString ());
217230 verifyNoMoreInteractions (in );
218231 }
219232
@@ -229,8 +242,89 @@ public void testPositionalReadPrematureEOF() throws IOException {
229242 FSDataInputStream in = mock (FSDataInputStream .class );
230243 when (in .read (position , buf , bufOffset , totalLen )).thenReturn (9 );
231244 when (in .read (position , buf , bufOffset , totalLen )).thenReturn (-1 );
245+ when (in .hasCapability (anyString ())).thenReturn (false );
232246 exception .expect (IOException .class );
233247 exception .expectMessage ("EOF" );
234248 BlockIOUtils .preadWithExtra (bb , in , position , necessaryLen , extraLen );
235249 }
250+
251+ /**
252+ * Determine if ByteBufferPositionedReadable API is available
253+ * .
254+ * @return true if FSDataInputStream implements ByteBufferPositionedReadable API.
255+ */
256+ private boolean isByteBufferPositionedReadable () {
257+ try {
258+ //long position, ByteBuffer buf
259+ FSDataInputStream .class .getMethod ("read" , long .class , ByteBuffer .class );
260+ } catch (NoSuchMethodException e ) {
261+ return false ;
262+ }
263+ return true ;
264+ }
265+
266+ public static class MyFSDataInputStream extends FSDataInputStream {
267+ public MyFSDataInputStream (InputStream in ) {
268+ super (in );
269+ }
270+
271+ // This is the ByteBufferPositionReadable API we want to test.
272+ // Because the API is only available in Hadoop 3.3, FSDataInputStream in older Hadoop
273+ // does not implement the interface, and it wouldn't compile trying to mock the method.
274+ // So explicitly declare the method here to make mocking possible.
275+ public int read (long position , ByteBuffer buf ) throws IOException {
276+ return 0 ;
277+ }
278+ }
279+
280+ @ Test
281+ public void testByteBufferPositionedReadable () throws IOException {
282+ assumeTrue ("Skip the test because ByteBufferPositionedReadable is not available" ,
283+ isByteBufferPositionedReadable ());
284+ long position = 0 ;
285+ int necessaryLen = 10 ;
286+ int extraLen = 1 ;
287+ int totalLen = necessaryLen + extraLen ;
288+ int firstReadLen = 6 ;
289+ int secondReadLen = totalLen - firstReadLen ;
290+ ByteBuffer buf = ByteBuffer .allocate (totalLen );
291+ ByteBuff bb = new SingleByteBuff (buf );
292+ MyFSDataInputStream in = mock (MyFSDataInputStream .class );
293+
294+ when (in .read (position , buf )).thenReturn (firstReadLen );
295+ when (in .read (firstReadLen , buf )).thenReturn (secondReadLen );
296+ when (in .hasCapability (anyString ())).thenReturn (true );
297+ boolean ret = BlockIOUtils .preadWithExtra (bb , in , position , necessaryLen , extraLen );
298+ assertTrue ("Expect true return when reading extra bytes succeeds" , ret );
299+ verify (in ).read (position , buf );
300+ verify (in ).read (firstReadLen , buf );
301+ verify (in ).hasCapability (anyString ());
302+ verifyNoMoreInteractions (in );
303+ }
304+
305+ @ Test
306+ public void testByteBufferPositionedReadableEOF () throws IOException {
307+ assumeTrue ("Skip the test because ByteBufferPositionedReadable is not available" ,
308+ isByteBufferPositionedReadable ());
309+ long position = 0 ;
310+ int necessaryLen = 10 ;
311+ int extraLen = 0 ;
312+ int totalLen = necessaryLen + extraLen ;
313+ int firstReadLen = 9 ;
314+ ByteBuffer buf = ByteBuffer .allocate (totalLen );
315+ ByteBuff bb = new SingleByteBuff (buf );
316+ MyFSDataInputStream in = mock (MyFSDataInputStream .class );
317+
318+ when (in .read (position , buf )).thenReturn (firstReadLen );
319+ when (in .read (position , buf )).thenReturn (-1 );
320+ when (in .hasCapability (anyString ())).thenReturn (true );
321+ exception .expect (IOException .class );
322+ exception .expectMessage ("EOF" );
323+ BlockIOUtils .preadWithExtra (bb , in , position , necessaryLen , extraLen );
324+
325+ verify (in ).read (position , buf );
326+ verify (in ).read (firstReadLen , buf );
327+ verify (in ).hasCapability (anyString ());
328+ verifyNoMoreInteractions (in );
329+ }
236330}
0 commit comments