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