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