001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNotEquals;
023import static org.junit.Assert.assertNotNull;
024import static org.junit.Assert.assertNull;
025import static org.junit.Assert.assertTrue;
026
027import java.io.File;
028import java.io.IOException;
029import java.util.List;
030import java.util.Random;
031import org.apache.hadoop.conf.Configuration;
032import org.apache.hadoop.fs.FSDataInputStream;
033import org.apache.hadoop.fs.FSDataOutputStream;
034import org.apache.hadoop.fs.FileStatus;
035import org.apache.hadoop.fs.FileSystem;
036import org.apache.hadoop.fs.Path;
037import org.apache.hadoop.fs.permission.FsPermission;
038import org.apache.hadoop.hbase.HBaseClassTestRule;
039import org.apache.hadoop.hbase.HBaseTestingUtility;
040import org.apache.hadoop.hbase.HConstants;
041import org.apache.hadoop.hbase.HDFSBlocksDistribution;
042import org.apache.hadoop.hbase.exceptions.DeserializationException;
043import org.apache.hadoop.hbase.fs.HFileSystem;
044import org.apache.hadoop.hbase.testclassification.MediumTests;
045import org.apache.hadoop.hbase.testclassification.MiscTests;
046import org.apache.hadoop.hdfs.DFSConfigKeys;
047import org.apache.hadoop.hdfs.DFSHedgedReadMetrics;
048import org.apache.hadoop.hdfs.DFSTestUtil;
049import org.apache.hadoop.hdfs.DistributedFileSystem;
050import org.apache.hadoop.hdfs.MiniDFSCluster;
051import org.junit.Assert;
052import org.junit.Before;
053import org.junit.ClassRule;
054import org.junit.Test;
055import org.junit.experimental.categories.Category;
056import org.slf4j.Logger;
057import org.slf4j.LoggerFactory;
058
059/**
060 * Test {@link FSUtils}.
061 */
062@Category({MiscTests.class, MediumTests.class})
063public class TestFSUtils {
064
065  @ClassRule
066  public static final HBaseClassTestRule CLASS_RULE =
067      HBaseClassTestRule.forClass(TestFSUtils.class);
068
069  private static final Logger LOG = LoggerFactory.getLogger(TestFSUtils.class);
070
071  private HBaseTestingUtility htu;
072  private FileSystem fs;
073  private Configuration conf;
074
075  @Before
076  public void setUp() throws IOException {
077    htu = new HBaseTestingUtility();
078    fs = htu.getTestFileSystem();
079    conf = htu.getConfiguration();
080  }
081
082  @Test public void testIsHDFS() throws Exception {
083    assertFalse(FSUtils.isHDFS(conf));
084    MiniDFSCluster cluster = null;
085    try {
086      cluster = htu.startMiniDFSCluster(1);
087      assertTrue(FSUtils.isHDFS(conf));
088    } finally {
089      if (cluster != null) cluster.shutdown();
090    }
091  }
092
093  private void WriteDataToHDFS(FileSystem fs, Path file, int dataSize)
094    throws Exception {
095    FSDataOutputStream out = fs.create(file);
096    byte [] data = new byte[dataSize];
097    out.write(data, 0, dataSize);
098    out.close();
099  }
100
101  @Test public void testcomputeHDFSBlocksDistribution() throws Exception {
102    final int DEFAULT_BLOCK_SIZE = 1024;
103    conf.setLong("dfs.blocksize", DEFAULT_BLOCK_SIZE);
104    MiniDFSCluster cluster = null;
105    Path testFile = null;
106
107    try {
108      // set up a cluster with 3 nodes
109      String hosts[] = new String[] { "host1", "host2", "host3" };
110      cluster = htu.startMiniDFSCluster(hosts);
111      cluster.waitActive();
112      FileSystem fs = cluster.getFileSystem();
113
114      // create a file with two blocks
115      testFile = new Path("/test1.txt");
116      WriteDataToHDFS(fs, testFile, 2*DEFAULT_BLOCK_SIZE);
117
118      // given the default replication factor is 3, the same as the number of
119      // datanodes; the locality index for each host should be 100%,
120      // or getWeight for each host should be the same as getUniqueBlocksWeights
121      final long maxTime = System.currentTimeMillis() + 2000;
122      boolean ok;
123      do {
124        ok = true;
125        FileStatus status = fs.getFileStatus(testFile);
126        HDFSBlocksDistribution blocksDistribution =
127          FSUtils.computeHDFSBlocksDistribution(fs, status, 0, status.getLen());
128        long uniqueBlocksTotalWeight =
129          blocksDistribution.getUniqueBlocksTotalWeight();
130        for (String host : hosts) {
131          long weight = blocksDistribution.getWeight(host);
132          ok = (ok && uniqueBlocksTotalWeight == weight);
133        }
134      } while (!ok && System.currentTimeMillis() < maxTime);
135      assertTrue(ok);
136      } finally {
137      htu.shutdownMiniDFSCluster();
138    }
139
140
141    try {
142      // set up a cluster with 4 nodes
143      String hosts[] = new String[] { "host1", "host2", "host3", "host4" };
144      cluster = htu.startMiniDFSCluster(hosts);
145      cluster.waitActive();
146      FileSystem fs = cluster.getFileSystem();
147
148      // create a file with three blocks
149      testFile = new Path("/test2.txt");
150      WriteDataToHDFS(fs, testFile, 3*DEFAULT_BLOCK_SIZE);
151
152      // given the default replication factor is 3, we will have total of 9
153      // replica of blocks; thus the host with the highest weight should have
154      // weight == 3 * DEFAULT_BLOCK_SIZE
155      final long maxTime = System.currentTimeMillis() + 2000;
156      long weight;
157      long uniqueBlocksTotalWeight;
158      do {
159        FileStatus status = fs.getFileStatus(testFile);
160        HDFSBlocksDistribution blocksDistribution =
161          FSUtils.computeHDFSBlocksDistribution(fs, status, 0, status.getLen());
162        uniqueBlocksTotalWeight = blocksDistribution.getUniqueBlocksTotalWeight();
163
164        String tophost = blocksDistribution.getTopHosts().get(0);
165        weight = blocksDistribution.getWeight(tophost);
166
167        // NameNode is informed asynchronously, so we may have a delay. See HBASE-6175
168      } while (uniqueBlocksTotalWeight != weight && System.currentTimeMillis() < maxTime);
169      assertTrue(uniqueBlocksTotalWeight == weight);
170
171    } finally {
172      htu.shutdownMiniDFSCluster();
173    }
174
175
176    try {
177      // set up a cluster with 4 nodes
178      String hosts[] = new String[] { "host1", "host2", "host3", "host4" };
179      cluster = htu.startMiniDFSCluster(hosts);
180      cluster.waitActive();
181      FileSystem fs = cluster.getFileSystem();
182
183      // create a file with one block
184      testFile = new Path("/test3.txt");
185      WriteDataToHDFS(fs, testFile, DEFAULT_BLOCK_SIZE);
186
187      // given the default replication factor is 3, we will have total of 3
188      // replica of blocks; thus there is one host without weight
189      final long maxTime = System.currentTimeMillis() + 2000;
190      HDFSBlocksDistribution blocksDistribution;
191      do {
192        FileStatus status = fs.getFileStatus(testFile);
193        blocksDistribution = FSUtils.computeHDFSBlocksDistribution(fs, status, 0, status.getLen());
194        // NameNode is informed asynchronously, so we may have a delay. See HBASE-6175
195      }
196      while (blocksDistribution.getTopHosts().size() != 3 && System.currentTimeMillis() < maxTime);
197      assertEquals("Wrong number of hosts distributing blocks.", 3,
198        blocksDistribution.getTopHosts().size());
199    } finally {
200      htu.shutdownMiniDFSCluster();
201    }
202  }
203
204  @Test
205  public void testVersion() throws DeserializationException, IOException {
206    final Path rootdir = htu.getDataTestDir();
207    final FileSystem fs = rootdir.getFileSystem(conf);
208    assertNull(FSUtils.getVersion(fs, rootdir));
209    // Write out old format version file.  See if we can read it in and convert.
210    Path versionFile = new Path(rootdir, HConstants.VERSION_FILE_NAME);
211    FSDataOutputStream s = fs.create(versionFile);
212    final String version = HConstants.FILE_SYSTEM_VERSION;
213    s.writeUTF(version);
214    s.close();
215    assertTrue(fs.exists(versionFile));
216    FileStatus [] status = fs.listStatus(versionFile);
217    assertNotNull(status);
218    assertTrue(status.length > 0);
219    String newVersion = FSUtils.getVersion(fs, rootdir);
220    assertEquals(version.length(), newVersion.length());
221    assertEquals(version, newVersion);
222    // File will have been converted. Exercise the pb format
223    assertEquals(version, FSUtils.getVersion(fs, rootdir));
224    FSUtils.checkVersion(fs, rootdir, true);
225  }
226
227  @Test
228  public void testPermMask() throws Exception {
229    final Path rootdir = htu.getDataTestDir();
230    final FileSystem fs = rootdir.getFileSystem(conf);
231    // default fs permission
232    FsPermission defaultFsPerm = FSUtils.getFilePermissions(fs, conf,
233        HConstants.DATA_FILE_UMASK_KEY);
234    // 'hbase.data.umask.enable' is false. We will get default fs permission.
235    assertEquals(FsPermission.getFileDefault(), defaultFsPerm);
236
237    conf.setBoolean(HConstants.ENABLE_DATA_FILE_UMASK, true);
238    // first check that we don't crash if we don't have perms set
239    FsPermission defaultStartPerm = FSUtils.getFilePermissions(fs, conf,
240        HConstants.DATA_FILE_UMASK_KEY);
241    // default 'hbase.data.umask'is 000, and this umask will be used when
242    // 'hbase.data.umask.enable' is true.
243    // Therefore we will not get the real fs default in this case.
244    // Instead we will get the starting point FULL_RWX_PERMISSIONS
245    assertEquals(new FsPermission(FSUtils.FULL_RWX_PERMISSIONS), defaultStartPerm);
246
247    conf.setStrings(HConstants.DATA_FILE_UMASK_KEY, "077");
248    // now check that we get the right perms
249    FsPermission filePerm = FSUtils.getFilePermissions(fs, conf,
250        HConstants.DATA_FILE_UMASK_KEY);
251    assertEquals(new FsPermission("700"), filePerm);
252
253    // then that the correct file is created
254    Path p = new Path("target" + File.separator + htu.getRandomUUID().toString());
255    try {
256      FSDataOutputStream out = FSUtils.create(conf, fs, p, filePerm, null);
257      out.close();
258      FileStatus stat = fs.getFileStatus(p);
259      assertEquals(new FsPermission("700"), stat.getPermission());
260      // and then cleanup
261    } finally {
262      fs.delete(p, true);
263    }
264  }
265
266  @Test
267  public void testDeleteAndExists() throws Exception {
268    final Path rootdir = htu.getDataTestDir();
269    final FileSystem fs = rootdir.getFileSystem(conf);
270    conf.setBoolean(HConstants.ENABLE_DATA_FILE_UMASK, true);
271    FsPermission perms = FSUtils.getFilePermissions(fs, conf, HConstants.DATA_FILE_UMASK_KEY);
272    // then that the correct file is created
273    String file = htu.getRandomUUID().toString();
274    Path p = new Path(htu.getDataTestDir(), "temptarget" + File.separator + file);
275    Path p1 = new Path(htu.getDataTestDir(), "temppath" + File.separator + file);
276    try {
277      FSDataOutputStream out = FSUtils.create(conf, fs, p, perms, null);
278      out.close();
279      assertTrue("The created file should be present", FSUtils.isExists(fs, p));
280      // delete the file with recursion as false. Only the file will be deleted.
281      FSUtils.delete(fs, p, false);
282      // Create another file
283      FSDataOutputStream out1 = FSUtils.create(conf, fs, p1, perms, null);
284      out1.close();
285      // delete the file with recursion as false. Still the file only will be deleted
286      FSUtils.delete(fs, p1, true);
287      assertFalse("The created file should be present", FSUtils.isExists(fs, p1));
288      // and then cleanup
289    } finally {
290      FSUtils.delete(fs, p, true);
291      FSUtils.delete(fs, p1, true);
292    }
293  }
294
295  @Test
296  public void testFilteredStatusDoesNotThrowOnNotFound() throws Exception {
297    MiniDFSCluster cluster = htu.startMiniDFSCluster(1);
298    try {
299      assertNull(FSUtils.listStatusWithStatusFilter(cluster.getFileSystem(), new Path("definitely/doesn't/exist"), null));
300    } finally {
301      cluster.shutdown();
302    }
303
304  }
305
306  @Test
307  public void testRenameAndSetModifyTime() throws Exception {
308    MiniDFSCluster cluster = htu.startMiniDFSCluster(1);
309    assertTrue(FSUtils.isHDFS(conf));
310
311    FileSystem fs = FileSystem.get(conf);
312    Path testDir = htu.getDataTestDirOnTestFS("testArchiveFile");
313
314    String file = htu.getRandomUUID().toString();
315    Path p = new Path(testDir, file);
316
317    FSDataOutputStream out = fs.create(p);
318    out.close();
319    assertTrue("The created file should be present", FSUtils.isExists(fs, p));
320
321    long expect = System.currentTimeMillis() + 1000;
322    assertNotEquals(expect, fs.getFileStatus(p).getModificationTime());
323
324    ManualEnvironmentEdge mockEnv = new ManualEnvironmentEdge();
325    mockEnv.setValue(expect);
326    EnvironmentEdgeManager.injectEdge(mockEnv);
327    try {
328      String dstFile = htu.getRandomUUID().toString();
329      Path dst = new Path(testDir , dstFile);
330
331      assertTrue(FSUtils.renameAndSetModifyTime(fs, p, dst));
332      assertFalse("The moved file should not be present", FSUtils.isExists(fs, p));
333      assertTrue("The dst file should be present", FSUtils.isExists(fs, dst));
334
335      assertEquals(expect, fs.getFileStatus(dst).getModificationTime());
336      cluster.shutdown();
337    } finally {
338      EnvironmentEdgeManager.reset();
339    }
340  }
341
342  @Test
343  public void testSetStoragePolicyDefault() throws Exception {
344    verifyNoHDFSApiInvocationForDefaultPolicy();
345    verifyFileInDirWithStoragePolicy(HConstants.DEFAULT_WAL_STORAGE_POLICY);
346  }
347
348  /**
349   * Note: currently the default policy is set to defer to HDFS and this case is to verify the
350   * logic, will need to remove the check if the default policy is changed
351   */
352  private void verifyNoHDFSApiInvocationForDefaultPolicy() {
353    FileSystem testFs = new AlwaysFailSetStoragePolicyFileSystem();
354    // There should be no exception thrown when setting to default storage policy, which indicates
355    // the HDFS API hasn't been called
356    try {
357      FSUtils.setStoragePolicy(testFs, new Path("non-exist"), HConstants.DEFAULT_WAL_STORAGE_POLICY,
358        true);
359    } catch (IOException e) {
360      Assert.fail("Should have bypassed the FS API when setting default storage policy");
361    }
362    // There should be exception thrown when given non-default storage policy, which indicates the
363    // HDFS API has been called
364    try {
365      FSUtils.setStoragePolicy(testFs, new Path("non-exist"), "HOT", true);
366      Assert.fail("Should have invoked the FS API but haven't");
367    } catch (IOException e) {
368      // expected given an invalid path
369    }
370  }
371
372  class AlwaysFailSetStoragePolicyFileSystem extends DistributedFileSystem {
373    @Override
374    public void setStoragePolicy(final Path src, final String policyName)
375            throws IOException {
376      throw new IOException("The setStoragePolicy method is invoked");
377    }
378  }
379
380  /* might log a warning, but still work. (always warning on Hadoop < 2.6.0) */
381  @Test
382  public void testSetStoragePolicyValidButMaybeNotPresent() throws Exception {
383    verifyFileInDirWithStoragePolicy("ALL_SSD");
384  }
385
386  final String INVALID_STORAGE_POLICY = "1772";
387
388  /* should log a warning, but still work. (different warning on Hadoop < 2.6.0) */
389  @Test
390  public void testSetStoragePolicyInvalid() throws Exception {
391    verifyFileInDirWithStoragePolicy(INVALID_STORAGE_POLICY);
392  }
393
394  // Here instead of TestCommonFSUtils because we need a minicluster
395  private void verifyFileInDirWithStoragePolicy(final String policy) throws Exception {
396    conf.set(HConstants.WAL_STORAGE_POLICY, policy);
397
398    MiniDFSCluster cluster = htu.startMiniDFSCluster(1);
399    try {
400      assertTrue(FSUtils.isHDFS(conf));
401
402      FileSystem fs = FileSystem.get(conf);
403      Path testDir = htu.getDataTestDirOnTestFS("testArchiveFile");
404      fs.mkdirs(testDir);
405
406      String storagePolicy =
407          conf.get(HConstants.WAL_STORAGE_POLICY, HConstants.DEFAULT_WAL_STORAGE_POLICY);
408      FSUtils.setStoragePolicy(fs, testDir, storagePolicy);
409
410      String file =htu.getRandomUUID().toString();
411      Path p = new Path(testDir, file);
412      WriteDataToHDFS(fs, p, 4096);
413      HFileSystem hfs = new HFileSystem(fs);
414      String policySet = hfs.getStoragePolicyName(p);
415      LOG.debug("The storage policy of path " + p + " is " + policySet);
416      if (policy.equals(HConstants.DEFER_TO_HDFS_STORAGE_POLICY)
417              || policy.equals(INVALID_STORAGE_POLICY)) {
418        String hdfsDefaultPolicy = hfs.getStoragePolicyName(hfs.getHomeDirectory());
419        LOG.debug("The default hdfs storage policy (indicated by home path: "
420                + hfs.getHomeDirectory() + ") is " + hdfsDefaultPolicy);
421        Assert.assertEquals(hdfsDefaultPolicy, policySet);
422      } else {
423        Assert.assertEquals(policy, policySet);
424      }
425      // will assert existance before deleting.
426      cleanupFile(fs, testDir);
427    } finally {
428      cluster.shutdown();
429    }
430  }
431
432  /**
433   * Ugly test that ensures we can get at the hedged read counters in dfsclient.
434   * Does a bit of preading with hedged reads enabled using code taken from hdfs TestPread.
435   * @throws Exception
436   */
437  @Test public void testDFSHedgedReadMetrics() throws Exception {
438    // Enable hedged reads and set it so the threshold is really low.
439    // Most of this test is taken from HDFS, from TestPread.
440    conf.setInt(DFSConfigKeys.DFS_DFSCLIENT_HEDGED_READ_THREADPOOL_SIZE, 5);
441    conf.setLong(DFSConfigKeys.DFS_DFSCLIENT_HEDGED_READ_THRESHOLD_MILLIS, 0);
442    conf.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, 4096);
443    conf.setLong(DFSConfigKeys.DFS_CLIENT_READ_PREFETCH_SIZE_KEY, 4096);
444    // Set short retry timeouts so this test runs faster
445    conf.setInt(DFSConfigKeys.DFS_CLIENT_RETRY_WINDOW_BASE, 0);
446    conf.setBoolean("dfs.datanode.transferTo.allowed", false);
447    MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf).numDataNodes(3).build();
448    // Get the metrics.  Should be empty.
449    DFSHedgedReadMetrics metrics = FSUtils.getDFSHedgedReadMetrics(conf);
450    assertEquals(0, metrics.getHedgedReadOps());
451    FileSystem fileSys = cluster.getFileSystem();
452    try {
453      Path p = new Path("preadtest.dat");
454      // We need > 1 blocks to test out the hedged reads.
455      DFSTestUtil.createFile(fileSys, p, 12 * blockSize, 12 * blockSize,
456        blockSize, (short) 3, seed);
457      pReadFile(fileSys, p);
458      cleanupFile(fileSys, p);
459      assertTrue(metrics.getHedgedReadOps() > 0);
460    } finally {
461      fileSys.close();
462      cluster.shutdown();
463    }
464  }
465
466
467  @Test
468  public void testCopyFilesParallel() throws Exception {
469    MiniDFSCluster cluster = htu.startMiniDFSCluster(1);
470    cluster.waitActive();
471    FileSystem fs = cluster.getFileSystem();
472    Path src = new Path("/src");
473    fs.mkdirs(src);
474    for (int i = 0; i < 50; i++) {
475      WriteDataToHDFS(fs, new Path(src, String.valueOf(i)), 1024);
476    }
477    Path sub = new Path(src, "sub");
478    fs.mkdirs(sub);
479    for (int i = 0; i < 50; i++) {
480      WriteDataToHDFS(fs, new Path(sub, String.valueOf(i)), 1024);
481    }
482    Path dst = new Path("/dst");
483    List<Path> allFiles = FSUtils.copyFilesParallel(fs, src, fs, dst, conf, 4);
484
485    assertEquals(102, allFiles.size());
486    FileStatus[] list = fs.listStatus(dst);
487    assertEquals(51, list.length);
488    FileStatus[] sublist = fs.listStatus(new Path(dst, "sub"));
489    assertEquals(50, sublist.length);
490  }
491
492  // Below is taken from TestPread over in HDFS.
493  static final int blockSize = 4096;
494  static final long seed = 0xDEADBEEFL;
495
496  private void pReadFile(FileSystem fileSys, Path name) throws IOException {
497    FSDataInputStream stm = fileSys.open(name);
498    byte[] expected = new byte[12 * blockSize];
499    Random rand = new Random(seed);
500    rand.nextBytes(expected);
501    // do a sanity check. Read first 4K bytes
502    byte[] actual = new byte[4096];
503    stm.readFully(actual);
504    checkAndEraseData(actual, 0, expected, "Read Sanity Test");
505    // now do a pread for the first 8K bytes
506    actual = new byte[8192];
507    doPread(stm, 0L, actual, 0, 8192);
508    checkAndEraseData(actual, 0, expected, "Pread Test 1");
509    // Now check to see if the normal read returns 4K-8K byte range
510    actual = new byte[4096];
511    stm.readFully(actual);
512    checkAndEraseData(actual, 4096, expected, "Pread Test 2");
513    // Now see if we can cross a single block boundary successfully
514    // read 4K bytes from blockSize - 2K offset
515    stm.readFully(blockSize - 2048, actual, 0, 4096);
516    checkAndEraseData(actual, (blockSize - 2048), expected, "Pread Test 3");
517    // now see if we can cross two block boundaries successfully
518    // read blockSize + 4K bytes from blockSize - 2K offset
519    actual = new byte[blockSize + 4096];
520    stm.readFully(blockSize - 2048, actual);
521    checkAndEraseData(actual, (blockSize - 2048), expected, "Pread Test 4");
522    // now see if we can cross two block boundaries that are not cached
523    // read blockSize + 4K bytes from 10*blockSize - 2K offset
524    actual = new byte[blockSize + 4096];
525    stm.readFully(10 * blockSize - 2048, actual);
526    checkAndEraseData(actual, (10 * blockSize - 2048), expected, "Pread Test 5");
527    // now check that even after all these preads, we can still read
528    // bytes 8K-12K
529    actual = new byte[4096];
530    stm.readFully(actual);
531    checkAndEraseData(actual, 8192, expected, "Pread Test 6");
532    // done
533    stm.close();
534    // check block location caching
535    stm = fileSys.open(name);
536    stm.readFully(1, actual, 0, 4096);
537    stm.readFully(4*blockSize, actual, 0, 4096);
538    stm.readFully(7*blockSize, actual, 0, 4096);
539    actual = new byte[3*4096];
540    stm.readFully(0*blockSize, actual, 0, 3*4096);
541    checkAndEraseData(actual, 0, expected, "Pread Test 7");
542    actual = new byte[8*4096];
543    stm.readFully(3*blockSize, actual, 0, 8*4096);
544    checkAndEraseData(actual, 3*blockSize, expected, "Pread Test 8");
545    // read the tail
546    stm.readFully(11*blockSize+blockSize/2, actual, 0, blockSize/2);
547    IOException res = null;
548    try { // read beyond the end of the file
549      stm.readFully(11*blockSize+blockSize/2, actual, 0, blockSize);
550    } catch (IOException e) {
551      // should throw an exception
552      res = e;
553    }
554    assertTrue("Error reading beyond file boundary.", res != null);
555
556    stm.close();
557  }
558
559  private void checkAndEraseData(byte[] actual, int from, byte[] expected, String message) {
560    for (int idx = 0; idx < actual.length; idx++) {
561      assertEquals(message+" byte "+(from+idx)+" differs. expected "+
562                        expected[from+idx]+" actual "+actual[idx],
563                        actual[idx], expected[from+idx]);
564      actual[idx] = 0;
565    }
566  }
567
568  private void doPread(FSDataInputStream stm, long position, byte[] buffer,
569      int offset, int length) throws IOException {
570    int nread = 0;
571    // long totalRead = 0;
572    // DFSInputStream dfstm = null;
573
574    /* Disable. This counts do not add up. Some issue in original hdfs tests?
575    if (stm.getWrappedStream() instanceof DFSInputStream) {
576      dfstm = (DFSInputStream) (stm.getWrappedStream());
577      totalRead = dfstm.getReadStatistics().getTotalBytesRead();
578    } */
579
580    while (nread < length) {
581      int nbytes =
582          stm.read(position + nread, buffer, offset + nread, length - nread);
583      assertTrue("Error in pread", nbytes > 0);
584      nread += nbytes;
585    }
586
587    /* Disable. This counts do not add up. Some issue in original hdfs tests?
588    if (dfstm != null) {
589      if (isHedgedRead) {
590        assertTrue("Expected read statistic to be incremented",
591          length <= dfstm.getReadStatistics().getTotalBytesRead() - totalRead);
592      } else {
593        assertEquals("Expected read statistic to be incremented", length, dfstm
594            .getReadStatistics().getTotalBytesRead() - totalRead);
595      }
596    }*/
597  }
598
599  private void cleanupFile(FileSystem fileSys, Path name) throws IOException {
600    assertTrue(fileSys.exists(name));
601    assertTrue(fileSys.delete(name, true));
602    assertTrue(!fileSys.exists(name));
603  }
604
605
606  private static final boolean STREAM_CAPABILITIES_IS_PRESENT;
607  static {
608    boolean tmp = false;
609    try {
610      Class.forName("org.apache.hadoop.fs.StreamCapabilities");
611      tmp = true;
612      LOG.debug("Test thought StreamCapabilities class was present.");
613    } catch (ClassNotFoundException exception) {
614      LOG.debug("Test didn't think StreamCapabilities class was present.");
615    } finally {
616      STREAM_CAPABILITIES_IS_PRESENT = tmp;
617    }
618  }
619
620  // Here instead of TestCommonFSUtils because we need a minicluster
621  @Test
622  public void checkStreamCapabilitiesOnHdfsDataOutputStream() throws Exception {
623    MiniDFSCluster cluster = htu.startMiniDFSCluster(1);
624    try (FileSystem filesystem = cluster.getFileSystem()) {
625      FSDataOutputStream stream = filesystem.create(new Path("/tmp/foobar"));
626      assertTrue(FSUtils.hasCapability(stream, "hsync"));
627      assertTrue(FSUtils.hasCapability(stream, "hflush"));
628      assertNotEquals("We expect HdfsDataOutputStream to say it has a dummy capability iff the " +
629          "StreamCapabilities class is not defined.",
630          STREAM_CAPABILITIES_IS_PRESENT,
631          FSUtils.hasCapability(stream, "a capability that hopefully HDFS doesn't add."));
632    } finally {
633      cluster.shutdown();
634    }
635  }
636
637}