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}