001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import java.io.File; 021import java.io.IOException; 022import java.lang.management.ManagementFactory; 023import java.lang.management.ThreadInfo; 024import java.lang.management.ThreadMXBean; 025import java.lang.reflect.InvocationTargetException; 026import java.util.Arrays; 027import java.util.Random; 028import java.util.Set; 029import java.util.concurrent.CountDownLatch; 030import java.util.concurrent.TimeoutException; 031import java.util.concurrent.atomic.AtomicInteger; 032import java.util.regex.Pattern; 033import org.apache.hadoop.fs.FileUtil; 034import org.apache.hadoop.util.Time; 035import org.junit.Assert; 036import org.mockito.invocation.InvocationOnMock; 037import org.mockito.stubbing.Answer; 038import org.slf4j.Logger; 039 040import org.apache.hbase.thirdparty.com.google.common.base.Joiner; 041import org.apache.hbase.thirdparty.com.google.common.base.Supplier; 042import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 043 044/** 045 * Test provides some very generic helpers which might be used across the tests 046 */ 047public abstract class GenericTestUtils { 048 049 private static final AtomicInteger sequence = new AtomicInteger(); 050 051 /** 052 * Extracts the name of the method where the invocation has happened 053 * @return String name of the invoking method 054 */ 055 public static String getMethodName() { 056 return Thread.currentThread().getStackTrace()[2].getMethodName(); 057 } 058 059 /** 060 * Generates a process-wide unique sequence number. 061 * @return a unique sequence number 062 */ 063 public static int uniqueSequenceId() { 064 return sequence.incrementAndGet(); 065 } 066 067 /** 068 * Assert that a given file exists. 069 */ 070 public static void assertExists(File f) { 071 Assert.assertTrue("File " + f + " should exist", f.exists()); 072 } 073 074 /** 075 * List all of the files in 'dir' that match the regex 'pattern'. Then check that this list is 076 * identical to 'expectedMatches'. 077 * @throws IOException if the dir is inaccessible 078 */ 079 public static void assertGlobEquals(File dir, String pattern, String... expectedMatches) 080 throws IOException { 081 082 Set<String> found = Sets.newTreeSet(); 083 for (File f : FileUtil.listFiles(dir)) { 084 if (f.getName().matches(pattern)) { 085 found.add(f.getName()); 086 } 087 } 088 Set<String> expectedSet = Sets.newTreeSet(Arrays.asList(expectedMatches)); 089 Assert.assertEquals("Bad files matching " + pattern + " in " + dir, 090 Joiner.on(",").join(expectedSet), Joiner.on(",").join(found)); 091 } 092 093 public static void waitFor(Supplier<Boolean> check, int checkEveryMillis, int waitForMillis) 094 throws TimeoutException, InterruptedException { 095 long st = Time.now(); 096 do { 097 boolean result = check.get(); 098 if (result) { 099 return; 100 } 101 102 Thread.sleep(checkEveryMillis); 103 } while (Time.now() - st < waitForMillis); 104 105 throw new TimeoutException("Timed out waiting for condition. " + "Thread diagnostics:\n" 106 + TimedOutTestsListener.buildThreadDiagnosticString()); 107 } 108 109 /** 110 * Mockito answer helper that triggers one latch as soon as the method is called, then waits on 111 * another before continuing. 112 */ 113 public static class DelayAnswer implements Answer<Object> { 114 private final Logger LOG; 115 116 private final CountDownLatch fireLatch = new CountDownLatch(1); 117 private final CountDownLatch waitLatch = new CountDownLatch(1); 118 private final CountDownLatch resultLatch = new CountDownLatch(1); 119 120 private final AtomicInteger fireCounter = new AtomicInteger(0); 121 private final AtomicInteger resultCounter = new AtomicInteger(0); 122 123 // Result fields set after proceed() is called. 124 private volatile Throwable thrown; 125 private volatile Object returnValue; 126 127 public DelayAnswer(Logger log) { 128 this.LOG = log; 129 } 130 131 /** 132 * Wait until the method is called. 133 */ 134 public void waitForCall() throws InterruptedException { 135 fireLatch.await(); 136 } 137 138 /** 139 * Tell the method to proceed. This should only be called after waitForCall() 140 */ 141 public void proceed() { 142 waitLatch.countDown(); 143 } 144 145 @Override 146 public Object answer(InvocationOnMock invocation) throws Throwable { 147 LOG.info("DelayAnswer firing fireLatch"); 148 fireCounter.getAndIncrement(); 149 fireLatch.countDown(); 150 try { 151 LOG.info("DelayAnswer waiting on waitLatch"); 152 waitLatch.await(); 153 LOG.info("DelayAnswer delay complete"); 154 } catch (InterruptedException ie) { 155 throw new IOException("Interrupted waiting on latch", ie); 156 } 157 return passThrough(invocation); 158 } 159 160 protected Object passThrough(InvocationOnMock invocation) throws Throwable { 161 try { 162 Object ret = invocation.callRealMethod(); 163 returnValue = ret; 164 return ret; 165 } catch (Throwable t) { 166 thrown = t; 167 throw t; 168 } finally { 169 resultCounter.incrementAndGet(); 170 resultLatch.countDown(); 171 } 172 } 173 174 /** 175 * After calling proceed(), this will wait until the call has completed and a result has been 176 * returned to the caller. 177 */ 178 public void waitForResult() throws InterruptedException { 179 resultLatch.await(); 180 } 181 182 /** 183 * After the call has gone through, return any exception that was thrown, or null if no 184 * exception was thrown. 185 */ 186 public Throwable getThrown() { 187 return thrown; 188 } 189 190 /** 191 * After the call has gone through, return the call's return value, or null in case it was void 192 * or an exception was thrown. 193 */ 194 public Object getReturnValue() { 195 return returnValue; 196 } 197 198 public int getFireCount() { 199 return fireCounter.get(); 200 } 201 202 public int getResultCount() { 203 return resultCounter.get(); 204 } 205 } 206 207 /** 208 * An Answer implementation that simply forwards all calls through to a delegate. This is useful 209 * as the default Answer for a mock object, to create something like a spy on an RPC proxy. For 210 * example: <code> 211 * NamenodeProtocol origNNProxy = secondary.getNameNode(); 212 * NamenodeProtocol spyNNProxy = Mockito.mock(NameNodeProtocol.class, 213 * new DelegateAnswer(origNNProxy); 214 * doThrow(...).when(spyNNProxy).getBlockLocations(...); 215 * ... 216 * </code> 217 */ 218 public static class DelegateAnswer implements Answer<Object> { 219 private final Object delegate; 220 private final Logger log; 221 222 public DelegateAnswer(Object delegate) { 223 this(null, delegate); 224 } 225 226 public DelegateAnswer(Logger log, Object delegate) { 227 this.log = log; 228 this.delegate = delegate; 229 } 230 231 @Override 232 public Object answer(InvocationOnMock invocation) throws Throwable { 233 try { 234 if (log != null) { 235 log.info("Call to " + invocation + " on " + delegate, new Exception("TRACE")); 236 } 237 return invocation.getMethod().invoke(delegate, invocation.getArguments()); 238 } catch (InvocationTargetException ite) { 239 throw ite.getCause(); 240 } 241 } 242 } 243 244 /** 245 * An Answer implementation which sleeps for a random number of milliseconds between 0 and a 246 * configurable value before delegating to the real implementation of the method. This can be 247 * useful for drawing out race conditions. 248 */ 249 public static class SleepAnswer implements Answer<Object> { 250 private final int maxSleepTime; 251 private static Random r = new Random(); 252 253 public SleepAnswer(int maxSleepTime) { 254 this.maxSleepTime = maxSleepTime; 255 } 256 257 @Override 258 public Object answer(InvocationOnMock invocation) throws Throwable { 259 boolean interrupted = false; 260 try { 261 Thread.sleep(r.nextInt(maxSleepTime)); 262 } catch (InterruptedException ie) { 263 interrupted = true; 264 } 265 try { 266 return invocation.callRealMethod(); 267 } finally { 268 if (interrupted) { 269 Thread.currentThread().interrupt(); 270 } 271 } 272 } 273 } 274 275 public static void assertMatches(String output, String pattern) { 276 Assert.assertTrue("Expected output to match /" + pattern + "/" + " but got:\n" + output, 277 Pattern.compile(pattern).matcher(output).find()); 278 } 279 280 public static void assertValueNear(long expected, long actual, long allowedError) { 281 assertValueWithinRange(expected - allowedError, expected + allowedError, actual); 282 } 283 284 public static void assertValueWithinRange(long expectedMin, long expectedMax, long actual) { 285 Assert.assertTrue( 286 "Expected " + actual + " to be in range (" + expectedMin + "," + expectedMax + ")", 287 expectedMin <= actual && actual <= expectedMax); 288 } 289 290 /** 291 * Assert that there are no threads running whose name matches the given regular expression. 292 * @param regex the regex to match against 293 */ 294 public static void assertNoThreadsMatching(String regex) { 295 Pattern pattern = Pattern.compile(regex); 296 ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); 297 298 ThreadInfo[] infos = threadBean.getThreadInfo(threadBean.getAllThreadIds(), 20); 299 for (ThreadInfo info : infos) { 300 if (info == null) continue; 301 if (pattern.matcher(info.getThreadName()).matches()) { 302 Assert.fail("Leaked thread: " + info + "\n" + Joiner.on("\n").join(info.getStackTrace())); 303 } 304 } 305 } 306}