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;
019
020import java.io.IOException;
021import java.nio.charset.StandardCharsets;
022import java.util.NavigableMap;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.fs.FileSystem;
025import org.apache.hadoop.fs.Path;
026import org.apache.hadoop.hbase.client.Durability;
027import org.apache.hadoop.hbase.client.Get;
028import org.apache.hadoop.hbase.client.Put;
029import org.apache.hadoop.hbase.client.Result;
030import org.apache.hadoop.hbase.client.Table;
031import org.apache.hadoop.hbase.log.HBaseMarkers;
032import org.apache.hadoop.hbase.regionserver.HRegion;
033import org.apache.hadoop.hbase.regionserver.Region;
034import org.apache.hadoop.hbase.regionserver.RegionAsTable;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.apache.hadoop.hbase.util.CommonFSUtils;
037import org.apache.hadoop.hbase.util.FSTableDescriptors;
038import org.apache.hadoop.hdfs.MiniDFSCluster;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042/**
043 * Abstract HBase test class. Initializes a few things that can come in handly like an
044 * HBaseConfiguration and filesystem.
045 * @deprecated since 2.0.0 and will be removed in 3.0.0. Write junit4 unit tests using
046 *             {@link HBaseTestingUtility}.
047 * @see HBaseTestingUtility
048 * @see <a href="https://issues.apache.org/jira/browse/HBASE-11912">HBASE-11912</a>
049 */
050@Deprecated
051public abstract class HBaseTestCase extends junit.framework.TestCase {
052  private static final Logger LOG = LoggerFactory.getLogger(HBaseTestCase.class);
053
054  protected final static byte[] fam1 = Bytes.toBytes("colfamily11");
055  protected final static byte[] fam2 = Bytes.toBytes("colfamily21");
056  protected final static byte[] fam3 = Bytes.toBytes("colfamily31");
057
058  protected static final byte[][] COLUMNS = { fam1, fam2, fam3 };
059
060  private boolean localfs = false;
061  protected static Path testDir = null;
062  protected FileSystem fs = null;
063  protected HRegion meta = null;
064  protected static final char FIRST_CHAR = 'a';
065  protected static final char LAST_CHAR = 'z';
066  protected static final String PUNCTUATION = "~`@#$%^&*()-_+=:;',.<>/?[]{}|";
067  protected static final byte[] START_KEY_BYTES = { FIRST_CHAR, FIRST_CHAR, FIRST_CHAR };
068  protected String START_KEY = new String(START_KEY_BYTES, HConstants.UTF8_CHARSET);
069  protected static final int MAXVERSIONS = 3;
070
071  protected final HBaseTestingUtility testUtil = new HBaseTestingUtility();
072
073  public volatile Configuration conf = testUtil.getConfiguration();
074  public final FSTableDescriptors fsTableDescriptors;
075  {
076    try {
077      fsTableDescriptors = new FSTableDescriptors(conf);
078    } catch (IOException e) {
079      throw new RuntimeException("Failed to init descriptors", e);
080    }
081  }
082
083  /** constructor */
084  public HBaseTestCase() {
085    super();
086  }
087
088  /**
089   * n
090   */
091  public HBaseTestCase(String name) {
092    super(name);
093  }
094
095  /**
096   * Note that this method must be called after the mini hdfs cluster has started or we end up with
097   * a local file system.
098   */
099  @Override
100  protected void setUp() throws Exception {
101    super.setUp();
102    localfs = (conf.get("fs.defaultFS", "file:///").compareTo("file:///") == 0);
103
104    if (fs == null) {
105      this.fs = FileSystem.get(conf);
106    }
107    try {
108      if (localfs) {
109        testDir = getUnitTestdir(getName());
110        if (fs.exists(testDir)) {
111          fs.delete(testDir, true);
112        }
113      } else {
114        testDir = CommonFSUtils.getRootDir(conf);
115      }
116    } catch (Exception e) {
117      LOG.error(HBaseMarkers.FATAL, "error during setup", e);
118      throw e;
119    }
120  }
121
122  @Override
123  protected void tearDown() throws Exception {
124    try {
125      if (localfs) {
126        if (this.fs.exists(testDir)) {
127          this.fs.delete(testDir, true);
128        }
129      }
130    } catch (Exception e) {
131      LOG.error(HBaseMarkers.FATAL, "error during tear down", e);
132    }
133    super.tearDown();
134  }
135
136  /**
137   * @see HBaseTestingUtility#getBaseTestDir n * @return directory to use for this test
138   */
139  protected Path getUnitTestdir(String testName) {
140    return testUtil.getDataTestDir(testName);
141  }
142
143  /**
144   * You must call close on the returned region and then close on the log file it created. Do
145   * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to close both the region and the WAL.
146   * nnn * @return An {@link HRegion} n
147   */
148  public HRegion createNewHRegion(HTableDescriptor desc, byte[] startKey, byte[] endKey)
149    throws IOException {
150    return createNewHRegion(desc, startKey, endKey, this.conf);
151  }
152
153  public HRegion createNewHRegion(HTableDescriptor desc, byte[] startKey, byte[] endKey,
154    Configuration conf) throws IOException {
155    HRegionInfo hri = new HRegionInfo(desc.getTableName(), startKey, endKey);
156    return HBaseTestingUtility.createRegionAndWAL(hri, testDir, conf, desc);
157  }
158
159  protected HRegion openClosedRegion(final HRegion closedRegion) throws IOException {
160    return HRegion.openHRegion(closedRegion, null);
161  }
162
163  /**
164   * Create a table of name {@code name} with {@link #COLUMNS} for families.
165   * @param name Name to give table.
166   * @return Column descriptor.
167   */
168  protected HTableDescriptor createTableDescriptor(final String name) {
169    return createTableDescriptor(name, MAXVERSIONS);
170  }
171
172  /**
173   * Create a table of name {@code name} with {@link #COLUMNS} for families.
174   * @param name     Name to give table.
175   * @param versions How many versions to allow per column.
176   * @return Column descriptor.
177   */
178  protected HTableDescriptor createTableDescriptor(final String name, final int versions) {
179    return createTableDescriptor(name, HColumnDescriptor.DEFAULT_MIN_VERSIONS, versions,
180      HConstants.FOREVER, HColumnDescriptor.DEFAULT_KEEP_DELETED);
181  }
182
183  /**
184   * Create a table of name {@code name} with {@link #COLUMNS} for families.
185   * @param name     Name to give table.
186   * @param versions How many versions to allow per column.
187   * @return Column descriptor.
188   */
189  protected HTableDescriptor createTableDescriptor(final String name, final int minVersions,
190    final int versions, final int ttl, KeepDeletedCells keepDeleted) {
191    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name));
192    for (byte[] cfName : new byte[][] { fam1, fam2, fam3 }) {
193      htd.addFamily(
194        new HColumnDescriptor(cfName).setMinVersions(minVersions).setMaxVersions(versions)
195          .setKeepDeletedCells(keepDeleted).setBlockCacheEnabled(false).setTimeToLive(ttl));
196    }
197    return htd;
198  }
199
200  /**
201   * Add content to region <code>r</code> on the passed column <code>column</code>. Adds data of the
202   * from 'aaa', 'aab', etc where key and value are the same. nnnn * @return count of what we added.
203   */
204  public static long addContent(final Region r, final byte[] columnFamily, final byte[] column)
205    throws IOException {
206    byte[] startKey = r.getRegionInfo().getStartKey();
207    byte[] endKey = r.getRegionInfo().getEndKey();
208    byte[] startKeyBytes = startKey;
209    if (startKeyBytes == null || startKeyBytes.length == 0) {
210      startKeyBytes = START_KEY_BYTES;
211    }
212    return addContent(new RegionAsTable(r), Bytes.toString(columnFamily), Bytes.toString(column),
213      startKeyBytes, endKey, -1);
214  }
215
216  public static long addContent(final Region r, final byte[] columnFamily) throws IOException {
217    return addContent(r, columnFamily, null);
218  }
219
220  /**
221   * Add content to region <code>r</code> on the passed column <code>column</code>. Adds data of the
222   * from 'aaa', 'aab', etc where key and value are the same. n * @return count of what we added.
223   */
224  public static long addContent(final Table updater, final String columnFamily) throws IOException {
225    return addContent(updater, columnFamily, START_KEY_BYTES, null);
226  }
227
228  public static long addContent(final Table updater, final String family, final String column)
229    throws IOException {
230    return addContent(updater, family, column, START_KEY_BYTES, null);
231  }
232
233  /**
234   * Add content to region <code>r</code> on the passed column <code>column</code>. Adds data of the
235   * from 'aaa', 'aab', etc where key and value are the same.
236   * @return count of what we added. n
237   */
238  public static long addContent(final Table updater, final String columnFamily,
239    final byte[] startKeyBytes, final byte[] endKey) throws IOException {
240    return addContent(updater, columnFamily, null, startKeyBytes, endKey, -1);
241  }
242
243  public static long addContent(final Table updater, final String family, String column,
244    final byte[] startKeyBytes, final byte[] endKey) throws IOException {
245    return addContent(updater, family, column, startKeyBytes, endKey, -1);
246  }
247
248  /**
249   * Add content to region <code>r</code> on the passed column <code>column</code>. Adds data of the
250   * from 'aaa', 'aab', etc where key and value are the same.
251   * @return count of what we added. n
252   */
253  public static long addContent(final Table updater, final String columnFamily, final String column,
254    final byte[] startKeyBytes, final byte[] endKey, final long ts) throws IOException {
255    long count = 0;
256    // Add rows of three characters. The first character starts with the
257    // 'a' character and runs up to 'z'. Per first character, we run the
258    // second character over same range. And same for the third so rows
259    // (and values) look like this: 'aaa', 'aab', 'aac', etc.
260    char secondCharStart = (char) startKeyBytes[1];
261    char thirdCharStart = (char) startKeyBytes[2];
262    EXIT: for (char c = (char) startKeyBytes[0]; c <= LAST_CHAR; c++) {
263      for (char d = secondCharStart; d <= LAST_CHAR; d++) {
264        for (char e = thirdCharStart; e <= LAST_CHAR; e++) {
265          byte[] t = new byte[] { (byte) c, (byte) d, (byte) e };
266          if (endKey != null && endKey.length > 0 && Bytes.compareTo(endKey, t) <= 0) {
267            break EXIT;
268          }
269          try {
270            Put put;
271            if (ts != -1) {
272              put = new Put(t, ts);
273            } else {
274              put = new Put(t);
275            }
276            try {
277              StringBuilder sb = new StringBuilder();
278              if (column != null && column.contains(":")) {
279                sb.append(column);
280              } else {
281                if (columnFamily != null) {
282                  sb.append(columnFamily);
283                  if (!columnFamily.endsWith(":")) {
284                    sb.append(":");
285                  }
286                  if (column != null) {
287                    sb.append(column);
288                  }
289                }
290              }
291              byte[][] split = CellUtil.parseColumn(Bytes.toBytes(sb.toString()));
292              if (split.length == 1) {
293                byte[] qualifier = new byte[0];
294                put.addColumn(split[0], qualifier, t);
295              } else {
296                put.addColumn(split[0], split[1], t);
297              }
298              put.setDurability(Durability.SKIP_WAL);
299              updater.put(put);
300              count++;
301            } catch (RuntimeException ex) {
302              ex.printStackTrace();
303              throw ex;
304            } catch (IOException ex) {
305              ex.printStackTrace();
306              throw ex;
307            }
308          } catch (RuntimeException ex) {
309            ex.printStackTrace();
310            throw ex;
311          } catch (IOException ex) {
312            ex.printStackTrace();
313            throw ex;
314          }
315        }
316        // Set start character back to FIRST_CHAR after we've done first loop.
317        thirdCharStart = FIRST_CHAR;
318      }
319      secondCharStart = FIRST_CHAR;
320    }
321    return count;
322  }
323
324  protected void assertResultEquals(final HRegion region, final byte[] row, final byte[] family,
325    final byte[] qualifier, final long timestamp, final byte[] value) throws IOException {
326    Get get = new Get(row);
327    get.setTimestamp(timestamp);
328    Result res = region.get(get);
329    NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> map = res.getMap();
330    byte[] res_value = map.get(family).get(qualifier).get(timestamp);
331
332    if (value == null) {
333      assertEquals(
334        Bytes.toString(family) + " " + Bytes.toString(qualifier) + " at timestamp " + timestamp,
335        null, res_value);
336    } else {
337      if (res_value == null) {
338        fail(Bytes.toString(family) + " " + Bytes.toString(qualifier) + " at timestamp " + timestamp
339          + "\" was expected to be \"" + Bytes.toStringBinary(value) + " but was null");
340      }
341      if (res_value != null) {
342        assertEquals(
343          Bytes.toString(family) + " " + Bytes.toString(qualifier) + " at timestamp " + timestamp,
344          value, new String(res_value, StandardCharsets.UTF_8));
345      }
346    }
347  }
348
349  /**
350   * Common method to close down a MiniDFSCluster and the associated file system n
351   */
352  public static void shutdownDfs(MiniDFSCluster cluster) {
353    if (cluster != null) {
354      LOG.info("Shutting down Mini DFS ");
355      try {
356        cluster.shutdown();
357      } catch (Exception e) {
358        /// Can get a java.lang.reflect.UndeclaredThrowableException thrown
359        // here because of an InterruptedException. Don't let exceptions in
360        // here be cause of test failure.
361      }
362      try {
363        FileSystem fs = cluster.getFileSystem();
364        if (fs != null) {
365          LOG.info("Shutting down FileSystem");
366          fs.close();
367        }
368        FileSystem.closeAll();
369      } catch (IOException e) {
370        LOG.error("error closing file system", e);
371      }
372    }
373  }
374
375  /**
376   * You must call {@link #closeRootAndMeta()} when done after calling this method. It does cleanup.
377   * n
378   */
379  protected void createMetaRegion() throws IOException {
380    FSTableDescriptors fsTableDescriptors = new FSTableDescriptors(conf);
381    meta = HBaseTestingUtility.createRegionAndWAL(HRegionInfo.FIRST_META_REGIONINFO, testDir, conf,
382      fsTableDescriptors.get(TableName.META_TABLE_NAME));
383  }
384
385  protected void closeRootAndMeta() throws IOException {
386    HBaseTestingUtility.closeRegionAndWAL(meta);
387  }
388
389  public static void assertByteEquals(byte[] expected, byte[] actual) {
390    if (Bytes.compareTo(expected, actual) != 0) {
391      throw new junit.framework.AssertionFailedError(
392        "expected:<" + Bytes.toString(expected) + "> but was:<" + Bytes.toString(actual) + ">");
393    }
394  }
395
396  public static void assertEquals(byte[] expected, byte[] actual) {
397    if (Bytes.compareTo(expected, actual) != 0) {
398      throw new junit.framework.AssertionFailedError("expected:<" + Bytes.toStringBinary(expected)
399        + "> but was:<" + Bytes.toStringBinary(actual) + ">");
400    }
401  }
402}