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.File;
022import java.io.IOException;
023import java.util.Arrays;
024import java.util.List;
025import java.util.UUID;
026
027import org.apache.commons.io.FileUtils;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.fs.Path;
030import org.apache.hadoop.hbase.Waiter.Predicate;
031import org.apache.hadoop.hbase.io.compress.Compression;
032import org.apache.yetus.audience.InterfaceAudience;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036/**
037 * Common helpers for testing HBase that do not depend on specific server/etc. things.
038 * @see org.apache.hadoop.hbase.HBaseCommonTestingUtility
039 *
040 */
041@InterfaceAudience.Public
042public class HBaseCommonTestingUtility {
043  protected static final Logger LOG = LoggerFactory.getLogger(HBaseCommonTestingUtility.class);
044
045  /** Compression algorithms to use in parameterized JUnit 4 tests */
046  public static final List<Object[]> COMPRESSION_ALGORITHMS_PARAMETERIZED =
047    Arrays.asList(new Object[][] {
048      { Compression.Algorithm.NONE },
049      { Compression.Algorithm.GZ }
050    });
051
052  /** This is for unit tests parameterized with a two booleans. */
053  public static final List<Object[]> BOOLEAN_PARAMETERIZED =
054      Arrays.asList(new Object[][] {
055          {false},
056          {true}
057      });
058
059  /** Compression algorithms to use in testing */
060  public static final Compression.Algorithm[] COMPRESSION_ALGORITHMS = {
061      Compression.Algorithm.NONE, Compression.Algorithm.GZ
062  };
063
064  protected Configuration conf;
065
066  public HBaseCommonTestingUtility() {
067    this(null);
068  }
069
070  public HBaseCommonTestingUtility(Configuration conf) {
071    this.conf = (conf == null ? HBaseConfiguration.create() : conf);
072  }
073
074  /**
075   * Returns this classes's instance of {@link Configuration}.
076   *
077   * @return Instance of Configuration.
078   */
079  public Configuration getConfiguration() {
080    return this.conf;
081  }
082
083  /**
084   * System property key to get base test directory value
085   */
086  public static final String BASE_TEST_DIRECTORY_KEY =
087      "test.build.data.basedirectory";
088
089  /**
090   * Default base directory for test output.
091   */
092  public static final String DEFAULT_BASE_TEST_DIRECTORY = "target/test-data";
093
094  /**
095   * Directory where we put the data for this instance of HBaseTestingUtility
096   */
097  private File dataTestDir = null;
098
099  /**
100   * @return Where to write test data on local filesystem, specific to
101   * the test.  Useful for tests that do not use a cluster.
102   * Creates it if it does not exist already.
103   */
104  public Path getDataTestDir() {
105    if (this.dataTestDir == null) {
106      setupDataTestDir();
107    }
108    return new Path(this.dataTestDir.getAbsolutePath());
109  }
110
111  /**
112   * @param subdirName
113   * @return Path to a subdirectory named <code>subdirName</code> under
114   * {@link #getDataTestDir()}.
115   * Does *NOT* create it if it does not exist.
116   */
117  public Path getDataTestDir(final String subdirName) {
118    return new Path(getDataTestDir(), subdirName);
119  }
120
121  /**
122   * Sets up a directory for a test to use.
123   *
124   * @return New directory path, if created.
125   */
126  protected Path setupDataTestDir() {
127    if (this.dataTestDir != null) {
128      LOG.warn("Data test dir already setup in " +
129          dataTestDir.getAbsolutePath());
130      return null;
131    }
132    Path testPath = getRandomDir();
133    this.dataTestDir = new File(testPath.toString()).getAbsoluteFile();
134    // Set this property so if mapreduce jobs run, they will use this as their home dir.
135    System.setProperty("test.build.dir", this.dataTestDir.toString());
136    if (deleteOnExit()) this.dataTestDir.deleteOnExit();
137
138    createSubDir("hbase.local.dir", testPath, "hbase-local-dir");
139
140    return testPath;
141  }
142
143  /**
144   * @return A dir with a random (uuid) name under the test dir
145   * @see #getBaseTestDir()
146   */
147  public Path getRandomDir() {
148    return new Path(getBaseTestDir(), UUID.randomUUID().toString());
149  }
150
151  protected void createSubDir(String propertyName, Path parent, String subDirName) {
152    Path newPath = new Path(parent, subDirName);
153    File newDir = new File(newPath.toString()).getAbsoluteFile();
154    if (deleteOnExit()) newDir.deleteOnExit();
155    conf.set(propertyName, newDir.getAbsolutePath());
156  }
157
158  /**
159   * @return True if we should delete testing dirs on exit.
160   */
161  boolean deleteOnExit() {
162    String v = System.getProperty("hbase.testing.preserve.testdir");
163    // Let default be true, to delete on exit.
164    return v == null ? true : !Boolean.parseBoolean(v);
165  }
166
167  /**
168   * @return True if we removed the test dirs
169   * @throws IOException
170   */
171  public boolean cleanupTestDir() throws IOException {
172    if (deleteDir(this.dataTestDir)) {
173      this.dataTestDir = null;
174      return true;
175    }
176    return false;
177  }
178
179  /**
180   * @param subdir Test subdir name.
181   * @return True if we removed the test dir
182   * @throws IOException
183   */
184  boolean cleanupTestDir(final String subdir) throws IOException {
185    if (this.dataTestDir == null) {
186      return false;
187    }
188    return deleteDir(new File(this.dataTestDir, subdir));
189  }
190
191  /**
192   * @return Where to write test data on local filesystem; usually
193   * {@link #DEFAULT_BASE_TEST_DIRECTORY}
194   * Should not be used by the unit tests, hence its's private.
195   * Unit test will use a subdirectory of this directory.
196   * @see #setupDataTestDir()
197   */
198  private Path getBaseTestDir() {
199    String PathName = System.getProperty(
200        BASE_TEST_DIRECTORY_KEY, DEFAULT_BASE_TEST_DIRECTORY);
201
202    return new Path(PathName);
203  }
204
205  /**
206   * @param dir Directory to delete
207   * @return True if we deleted it.
208   * @throws IOException
209   */
210  boolean deleteDir(final File dir) throws IOException {
211    if (dir == null || !dir.exists()) {
212      return true;
213    }
214    int ntries = 0;
215    do {
216      ntries += 1;
217      try {
218        if (deleteOnExit()) FileUtils.deleteDirectory(dir);
219        return true;
220      } catch (IOException ex) {
221        LOG.warn("Failed to delete " + dir.getAbsolutePath());
222      } catch (IllegalArgumentException ex) {
223        LOG.warn("Failed to delete " + dir.getAbsolutePath(), ex);
224      }
225    } while (ntries < 30);
226
227    return false;
228  }
229
230  /**
231   * Wrapper method for {@link Waiter#waitFor(Configuration, long, Predicate)}.
232   */
233  public <E extends Exception> long waitFor(long timeout, Predicate<E> predicate)
234      throws E {
235    return Waiter.waitFor(this.conf, timeout, predicate);
236  }
237
238  /**
239   * Wrapper method for {@link Waiter#waitFor(Configuration, long, long, Predicate)}.
240   */
241  public <E extends Exception> long waitFor(long timeout, long interval, Predicate<E> predicate)
242      throws E {
243    return Waiter.waitFor(this.conf, timeout, interval, predicate);
244  }
245
246  /**
247   * Wrapper method for {@link Waiter#waitFor(Configuration, long, long, boolean, Predicate)}.
248   */
249  public <E extends Exception> long waitFor(long timeout, long interval,
250      boolean failIfTimeout, Predicate<E> predicate) throws E {
251    return Waiter.waitFor(this.conf, timeout, interval, failIfTimeout, predicate);
252  }
253}