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