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.hamcrest.MatcherAssert.assertThat;
021import static org.hamcrest.Matchers.startsWith;
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertNull;
024import static org.junit.Assert.assertThrows;
025
026import java.io.IOException;
027import org.apache.hadoop.fs.FSDataInputStream;
028import org.apache.hadoop.fs.FSDataOutputStream;
029import org.apache.hadoop.fs.FileSystem;
030import org.apache.hadoop.fs.Path;
031import org.apache.hadoop.hbase.HBaseClassTestRule;
032import org.apache.hadoop.hbase.HBaseCommonTestingUtility;
033import org.apache.hadoop.hbase.testclassification.MiscTests;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.junit.AfterClass;
036import org.junit.Before;
037import org.junit.BeforeClass;
038import org.junit.ClassRule;
039import org.junit.Rule;
040import org.junit.Test;
041import org.junit.experimental.categories.Category;
042import org.junit.rules.TestName;
043
044import org.apache.hbase.thirdparty.com.google.common.io.ByteStreams;
045
046@Category({ MiscTests.class, SmallTests.class })
047public class TestRotateFile {
048
049  @ClassRule
050  public static final HBaseClassTestRule CLASS_RULE =
051    HBaseClassTestRule.forClass(TestRotateFile.class);
052
053  private static HBaseCommonTestingUtility UTIL = new HBaseCommonTestingUtility();
054
055  private static FileSystem FS;
056
057  private Path dir;
058
059  private RotateFile rotateFile;
060
061  @Rule
062  public final TestName name = new TestName();
063
064  @BeforeClass
065  public static void setUpBeforeClass() throws IOException {
066    FS = FileSystem.get(UTIL.getConfiguration());
067  }
068
069  @AfterClass
070  public static void tearDownAfterClass() {
071    UTIL.cleanupTestDir();
072  }
073
074  @Before
075  public void setUp() throws IOException {
076    dir = UTIL.getDataTestDir(name.getMethodName());
077    if (!FS.mkdirs(dir)) {
078      throw new IOException("Can not create dir " + dir);
079    }
080    rotateFile = new RotateFile(FS, dir, name.getMethodName(), 1024);
081    assertNull(rotateFile.read());
082  }
083
084  @Test
085  public void testSimpleReadWrite() throws IOException {
086    for (int i = 0; i < 10; i++) {
087      rotateFile.write(Bytes.toBytes(i));
088      assertEquals(i, Bytes.toInt(rotateFile.read()));
089    }
090    rotateFile.delete();
091    assertNull(rotateFile.read());
092  }
093
094  @Test
095  public void testCompareTimestamp() throws IOException {
096    long now = EnvironmentEdgeManager.currentTime();
097    rotateFile.write(Bytes.toBytes(10));
098    Path file = FS.listStatus(dir)[0].getPath();
099    rotateFile.write(Bytes.toBytes(100));
100
101    // put a fake file with a less timestamp there
102    RotateFile.write(FS, file, now - 1, Bytes.toBytes(10));
103    assertEquals(100, Bytes.toInt(rotateFile.read()));
104
105    // put a fake file with a greater timestamp there
106    RotateFile.write(FS, file, EnvironmentEdgeManager.currentTime() + 100, Bytes.toBytes(10));
107    assertEquals(10, Bytes.toInt(rotateFile.read()));
108  }
109
110  @Test
111  public void testMaxFileSize() throws IOException {
112    assertThrows(IOException.class, () -> rotateFile.write(new byte[1025]));
113    // put a file greater than max file size
114    rotateFile.write(Bytes.toBytes(10));
115    Path file = FS.listStatus(dir)[0].getPath();
116    RotateFile.write(FS, file, EnvironmentEdgeManager.currentTime(), new byte[1025]);
117    assertThrows(IOException.class, () -> rotateFile.read());
118  }
119
120  @Test
121  public void testNotEnoughData() throws IOException {
122    rotateFile.write(Bytes.toBytes(10));
123    assertEquals(10, Bytes.toInt(rotateFile.read()));
124    // remove the last byte
125    Path file = FS.listStatus(dir)[0].getPath();
126    byte[] data;
127    try (FSDataInputStream in = FS.open(file)) {
128      data = ByteStreams.toByteArray(in);
129    }
130    try (FSDataOutputStream out = FS.create(file, true)) {
131      out.write(data, 0, data.length - 1);
132    }
133    // should hit EOF so read nothing
134    assertNull(rotateFile.read());
135  }
136
137  @Test
138  public void testChecksumMismatch() throws IOException {
139    rotateFile.write(Bytes.toBytes(10));
140    assertEquals(10, Bytes.toInt(rotateFile.read()));
141    // mess up one byte
142    Path file = FS.listStatus(dir)[0].getPath();
143    byte[] data;
144    try (FSDataInputStream in = FS.open(file)) {
145      data = ByteStreams.toByteArray(in);
146    }
147    data[4]++;
148    try (FSDataOutputStream out = FS.create(file, true)) {
149      out.write(data, 0, data.length);
150    }
151    // should get checksum mismatch
152    IOException error = assertThrows(IOException.class, () -> rotateFile.read());
153    assertThat(error.getMessage(), startsWith("Checksum mismatch"));
154  }
155}