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