Updates to r6 of littlemock, concrete classes.

- Updates source to r6.

Highlights:
- Can now mock concrete classes if you have dexmaker.jar on your
  classpath.
- Can use blocking verify() calls with timeout() method.
- Thread-safe mocks, and only one thread can verify() and stub().

Change-Id: I42f55cb33d2ed99097317705a35e73e63f8a864f
diff --git a/README b/README
index fba3f30..6ca0de4 100644
--- a/README
+++ b/README
@@ -8,8 +8,8 @@
 
 Description:
 Mocking framework. Looks very like Mockito (see http://mockito.org).
-Has a number of restrictions - mocks interfaces only, no class
-generation. Has no dependencies, consequently can be used on Android.
+Has a number of restrictions.
+Can mock concrete classes, requires dexmaker.jar on classpath.
 
 Local Modifications:
 No modifications.
diff --git a/src/com/google/testing/littlemock/AppDataDirGuesser.java b/src/com/google/testing/littlemock/AppDataDirGuesser.java
new file mode 100644
index 0000000..0fdd475
--- /dev/null
+++ b/src/com/google/testing/littlemock/AppDataDirGuesser.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.testing.littlemock;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for helping guess the application data directory.
+ */
+public class AppDataDirGuesser {
+
+  /** A single default instance of app data dir guesser, for overriding if you really need to. */
+  private static volatile AppDataDirGuesser sInjectableInstance = new AppDataDirGuesser();
+
+  public static void setInstance(AppDataDirGuesser instance) {
+    sInjectableInstance = instance;
+  }
+
+  public static AppDataDirGuesser getsInstance() {
+    return sInjectableInstance;
+  }
+
+  public File guessSuitableDirectoryForGeneratedClasses() {
+    try {
+      ClassLoader classLoader = AppDataDirGuesser.class.getClassLoader();
+      // Check that we have an instance of the PathClassLoader.
+      Class<?> clazz = Class.forName("dalvik.system.PathClassLoader");
+      clazz.cast(classLoader);
+      // Use the toString() method to calculate the data directory.
+      String pathFromThisClassLoader = getPathFromThisClassLoader(classLoader);
+      File[] results = guessPath(pathFromThisClassLoader);
+      if (results.length > 0) {
+        return results[0];
+      }
+    } catch (ClassCastException e) {
+      // Fall through, we will return null.
+    } catch (ClassNotFoundException e) {
+      // Fall through, we will return null.
+    }
+    return null;
+  }
+
+  private String getPathFromThisClassLoader(ClassLoader classLoader) {
+    // Parsing toString() method: yuck.  But no other way to get the path.
+    // Strip out the bit between angle brackets, that's our path.
+    String result = classLoader.toString();
+    int index = result.lastIndexOf('[');
+    result = (index == -1) ? result : result.substring(index + 1);
+    index = result.indexOf(']');
+    return (index == -1) ? result : result.substring(0, index);
+  }
+
+  // @VisibleForTesting
+  File[] guessPath(String input) {
+    List<File> results = new ArrayList<File>();
+    for (String potential : input.split(":")) {
+      if (!potential.startsWith("/data/app/")) {
+        continue;
+      }
+      int start = "/data/app/".length();
+      int end = potential.lastIndexOf(".apk");
+      if (end != potential.length() - 4) {
+        continue;
+      }
+      int dash = potential.indexOf("-");
+      if (dash != -1) {
+        end = dash;
+      }
+      File file = new File("/data/data/" + potential.substring(start, end) + "/cache");
+      if (isWriteableDirectory(file)) {
+        results.add(file);
+      }
+    }
+    return results.toArray(new File[results.size()]);
+  }
+
+  // @VisibleForTesting
+  boolean isWriteableDirectory(File file) {
+    return file.isDirectory() && file.canWrite();
+  }
+}
diff --git a/src/com/google/testing/littlemock/LittleMock.java b/src/com/google/testing/littlemock/LittleMock.java
index 03e2c7a..c8f2b2f 100644
--- a/src/com/google/testing/littlemock/LittleMock.java
+++ b/src/com/google/testing/littlemock/LittleMock.java
@@ -16,6 +16,7 @@
 
 package com.google.testing.littlemock;
 
+import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
@@ -23,10 +24,11 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Very lightweight and simple mocking framework, inspired by Mockito, http://mockito.org.
@@ -35,9 +37,6 @@
  * <ul>
  *   <li>Doesn't support mocking concrete classes, <b>interfaces only</b>.</li>
  *
- *   <li><b>Is not thread-safe.</b> You'll need to provide your own synchronization if you want to
- *   run multi-threaded unit tests.</li>
- *
  *   <li>It supports only a <b>small subset</b> of the APIs provided by Mockito and other mocking
  *   frameworks.</li>
  * </ul>
@@ -149,11 +148,21 @@
     if (howManyTimes == null) {
       throw new IllegalArgumentException("Can't pass null for howManyTimes parameter");
     }
+    DefaultInvocationHandler handler = getHandlerFrom(mock);
+    checkState(handler.mHowManyTimes == null, "Unfinished verify() statements");
+    checkState(handler.mStubbingAction == null, "Unfinished stubbing statements");
     checkNoMatchers();
-    getHandlerFrom(mock).mHowManyTimes = howManyTimes;
-    return mock;
+    handler.mHowManyTimes = howManyTimes;
+    sUnfinishedCallCounts.add(howManyTimes);
+    return handler.<T>getVerifyingMock();
   }
 
+  /** The list of outstanding calls to verify() that haven't finished, used to check for errors. */
+  private static List<CallCount> sUnfinishedCallCounts = new ArrayList<CallCount>();
+
+  /** The list of outstanding calls to when() that haven't finished, used to check for errors. */
+  private static List<Action> sUnfinishedStubbingActions = new ArrayList<Action>();
+
   /** Begins a verification step for exactly one method call. */
   public static <T> T verify(T mock) { return verify(mock, times(1)); }
 
@@ -161,7 +170,7 @@
   public static void verifyZeroInteractions(Object... mocks) {
     checkNoMatchers();
     for (Object mock : mocks) {
-      LinkedList<MethodCall> mMethodCalls = getHandlerFrom(mock).mRecordedCalls;
+      List<MethodCall> mMethodCalls = getHandlerFrom(mock).mRecordedCalls;
       expect(mMethodCalls.isEmpty(), "Mock expected zero interactions, had " + mMethodCalls);
     }
   }
@@ -200,7 +209,12 @@
   /** Creates a {@link CallCount} that matches exactly the given number of calls. */
   public static CallCount times(long n) { return new CallCount(n, n); }
 
-  /** Creates a {@link CallCount} that only matches if the method was never called. */
+  /** Claims that the verified call must happen before the given timeout. */
+  public static Timeout timeout(long timeoutMillis) {
+    return new Timeout(1, 1, timeoutMillis);
+  }
+
+/** Creates a {@link CallCount} that only matches if the method was never called. */
   public static CallCount never() { return new CallCount(0, 0); }
 
   /** Creates a {@link CallCount} that matches at least one method call. */
@@ -362,8 +376,9 @@
   }
 
   /** Creates a mock, more easily done via the {@link #initMocks(Object)} method. */
+  @SuppressWarnings("unchecked")
   private static <T> T mock(Class<T> clazz, String fieldName) {
-    return LittleMock.<T>newProxy(clazz, new DefaultInvocationHandler(clazz, fieldName));
+    return (T) createProxy(clazz, new DefaultInvocationHandler(clazz, fieldName));
   }
 
   /** Pick a suitable name for a field of the given clazz. */
@@ -380,7 +395,19 @@
   }
 
   /** Use this in tear down to check for programming errors. */
-  public static void checkForProgrammingErrorsDuringTearDown() { checkNoMatchers(); }
+  public static void checkForProgrammingErrorsDuringTearDown() {
+    checkNoMatchers();
+    checkNoUnfinishedCalls(sUnfinishedCallCounts, "verify()");
+    checkNoUnfinishedCalls(sUnfinishedStubbingActions, "stubbing");
+  }
+
+  /** Helper function to check that there are no verify or stubbing commands outstanding. */
+  private static void checkNoUnfinishedCalls(List<?> list, String type) {
+    if (!list.isEmpty()) {
+      list.clear();
+      throw new IllegalStateException("Unfinished " + type + " statements");
+    }
+  }
 
   /** Implementation of {@link Behaviour}. */
   private static class BehaviourImpl implements Behaviour {
@@ -392,8 +419,12 @@
 
     @Override
     public <T> T when(T mock) {
-      getHandlerFrom(mock).mStubbingAction = mAction;
-      return mock;
+      DefaultInvocationHandler handler = getHandlerFrom(mock);
+      checkState(handler.mHowManyTimes == null, "Unfinished verify() statements");
+      checkState(handler.mStubbingAction == null, "Unfinished stubbing statements");
+      handler.mStubbingAction = mAction;
+      sUnfinishedStubbingActions.add(mAction);
+      return handler.<T>getStubbingMock();
     }
   }
 
@@ -478,9 +509,10 @@
     private final String mFieldName;
 
     /** The list of method calls executed on the mock. */
-    private LinkedList<MethodCall> mRecordedCalls = new LinkedList<MethodCall>();
+    private List<MethodCall> mRecordedCalls = new CopyOnWriteArrayList<MethodCall>();
     /** The list of method calls that were stubbed out and their corresponding actions. */
-    private LinkedList<StubbedCall> mStubbedCalls = new LinkedList<StubbedCall>();
+    private List<StubbedCall> mStubbedCalls = new CopyOnWriteArrayList<StubbedCall>();
+
     /**
      * The number of times a given call should be verified.
      *
@@ -490,6 +522,7 @@
      * <p>It is reset to null once the verification has occurred.
      */
     private CallCount mHowManyTimes = null;
+
     /**
      * The action to be associated with the stubbed method.
      *
@@ -498,6 +531,12 @@
      */
     private Action mStubbingAction = null;
 
+    /** Dynamic proxy used to verify calls against this mock. */
+    private final Object mVerifyingMock;
+
+    /** Dynamic proxy used to stub calls against this mock. */
+    private final Object mStubbingMock;
+
     /**
      * Creates a new invocation handler for an object.
      *
@@ -509,24 +548,84 @@
     public DefaultInvocationHandler(Class<?> clazz, String fieldName) {
       mClazz = clazz;
       mFieldName = fieldName;
+      mVerifyingMock = createVerifyingMock(clazz);
+      mStubbingMock = createStubbingMock(clazz);
+    }
+
+    // Safe if you call getHandlerFrom(mock).getVerifyingMock(), since this is guaranteed to be
+    // of the same type as mock itself.
+    @SuppressWarnings("unchecked")
+    public <T> T getVerifyingMock() {
+      return (T) mVerifyingMock;
+    }
+
+    // Safe if you call getHandlerFrom(mock).getStubbingMock(), since this is guaranteed to be
+    // of the same type as mock itself.
+    @SuppressWarnings("unchecked")
+    public <T> T getStubbingMock() {
+      return (T) mStubbingMock;
+    }
+
+    /** Used to check that we always stub and verify from the same thread. */
+    private AtomicReference<Thread> mCurrentThread = new AtomicReference<Thread>();
+
+    /** Check that we are stubbing and verifying always from the same thread. */
+    private void checkThread() {
+      Thread currentThread = Thread.currentThread();
+      mCurrentThread.compareAndSet(null, currentThread);
+      if (mCurrentThread.get() != currentThread) {
+        throw new IllegalStateException("Must always mock and stub from one thread only.  "
+            + "This thread: " + currentThread + ", the other thread: " + mCurrentThread.get());
+      }
+    }
+
+    /** Generate the dynamic proxy that will handle verify invocations. */
+    private Object createVerifyingMock(Class<?> clazz) {
+      return createProxy(clazz, new InvocationHandler() {
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+          checkThread();
+          expect(mHowManyTimes != null, "verifying mock doesn't know how many times");
+          try {
+            ArgumentMatcher[] matchers = checkClearAndGetMatchers(method);
+            StackTraceElement callSite = new Exception().getStackTrace()[2];
+            MethodCall methodCall = new MethodCall(method, callSite, args);
+            innerVerify(method, matchers, methodCall, proxy, callSite, mHowManyTimes);
+            return defaultReturnValue(method.getReturnType());
+          } finally {
+            sUnfinishedCallCounts.remove(mHowManyTimes);
+            mHowManyTimes = null;
+          }
+        }
+      });
+    }
+
+    /** Generate the dynamic proxy that will handle stubbing invocations. */
+    private Object createStubbingMock(Class<?> clazz) {
+      return createProxy(clazz, new InvocationHandler() {
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+          checkThread();
+          expect(mStubbingAction != null, "stubbing mock doesn't know what action to perform");
+          try {
+            ArgumentMatcher[] matchers = checkClearAndGetMatchers(method);
+            StackTraceElement callSite = new Exception().getStackTrace()[2];
+            MethodCall methodCall = new MethodCall(method, callSite, args);
+            innerStub(method, matchers, methodCall, callSite, mStubbingAction);
+            return defaultReturnValue(method.getReturnType());
+          } finally {
+            sUnfinishedStubbingActions.remove(mStubbingAction);
+            mStubbingAction = null;
+          }
+        }
+      });
     }
 
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-      ArgumentMatcher[] matchers = checkClearAndGetMatchers(method);
       StackTraceElement callSite = new Exception().getStackTrace()[2];
       MethodCall methodCall = new MethodCall(method, callSite, args);
-      if (mHowManyTimes != null) {
-        innerVerify(method, matchers, methodCall, proxy, callSite);
-        mHowManyTimes = null;
-        return defaultReturnValue(method.getReturnType());
-      } else if (mStubbingAction != null) {
-        innerStub(method, matchers, methodCall, callSite);
-        mStubbingAction = null;
-        return defaultReturnValue(method.getReturnType());
-      } else {
-        return innerRecord(method, args, methodCall, proxy, callSite);
-      }
+      return innerRecord(method, args, methodCall, proxy, callSite);
     }
 
     /**
@@ -579,13 +678,12 @@
     }
 
     private void innerStub(Method method, final ArgumentMatcher[] matchers, MethodCall methodCall,
-        StackTraceElement callSite) {
-      final Action stubbingAction = mStubbingAction;
+        StackTraceElement callSite, final Action stubbingAction) {
       checkSpecialObjectMethods(method, "stub");
       checkThisActionCanBeUsedForThisMethod(method, stubbingAction, callSite);
       if (matchers.length == 0) {
         // If there are no matchers, then this is a simple stubbed method call with an action.
-        mStubbedCalls.addFirst(new StubbedCall(methodCall, stubbingAction));
+        mStubbedCalls.add(0, new StubbedCall(methodCall, stubbingAction));
         return;
       }
       // If there are matchers, then we need to make a new method call which matches only
@@ -607,7 +705,7 @@
           return stubbingAction.getReturnType();
         }
       };
-      mStubbedCalls.addFirst(new StubbedCall(matchMatchersMethodCall, setCapturesThenAction));
+      mStubbedCalls.add(0, new StubbedCall(matchMatchersMethodCall, setCapturesThenAction));
     }
 
     private void checkThisActionCanBeUsedForThisMethod(Method method, final Action stubbingAction,
@@ -645,8 +743,32 @@
     }
 
     private void innerVerify(Method method, ArgumentMatcher[] matchers, MethodCall methodCall,
-            Object proxy, StackTraceElement callSite) {
+        Object proxy, StackTraceElement callSite, CallCount callCount) {
       checkSpecialObjectMethods(method, "verify");
+      int total = countMatchingInvocations(method, matchers, methodCall);
+      long callTimeout = callCount.getTimeout();
+      if (callTimeout > 0) {
+        long endTime = System.currentTimeMillis() + callTimeout;
+        while (!callCount.matches(total)) {
+          try {
+            Thread.sleep(1);
+          } catch (InterruptedException e) {
+            fail("interrupted whilst waiting to verify");
+          }
+          if (System.currentTimeMillis() > endTime) {
+            fail(formatFailedVerifyMessage(methodCall, total, callTimeout, callCount));
+          }
+          total = countMatchingInvocations(method, matchers, methodCall);
+        }
+      } else {
+        if (!callCount.matches(total)) {
+          fail(formatFailedVerifyMessage(methodCall, total, 0, callCount));
+        }
+      }
+    }
+
+    private int countMatchingInvocations(Method method, ArgumentMatcher[] matchers,
+        MethodCall methodCall) {
       int total = 0;
       for (MethodCall call : mRecordedCalls) {
         if (call.mMethod.equals(method)) {
@@ -658,17 +780,22 @@
           }
         }
       }
-      expect(mHowManyTimes.matches(total), formatFailedVerifyMessage(methodCall, total));
+      return total;
     }
 
-    private String formatFailedVerifyMessage(MethodCall methodCall, int total) {
+    private String formatFailedVerifyMessage(MethodCall methodCall, int total, long timeoutMillis,
+        CallCount callCount) {
       StringBuffer sb = new StringBuffer();
-      sb.append("\nExpected ").append(mHowManyTimes).append(" to:");
+      sb.append("\nExpected ").append(callCount);
+      if (timeoutMillis > 0) {
+        sb.append(" within " + timeoutMillis + "ms");
+      }
+      sb.append(" to:");
       appendDebugStringForMethodCall(sb, methodCall.mMethod,
           methodCall.mElement, mFieldName, false);
       sb.append("\n\n");
       if (mRecordedCalls.size() == 0) {
-        sb.append("No method calls happened to this mock");
+        sb.append("No method calls happened on this mock");
       } else {
         sb.append("Method calls that did happen:");
         for (MethodCall recordedCall : mRecordedCalls) {
@@ -768,10 +895,10 @@
 
   /** Encapsulates the number of times a method is called, between upper and lower bounds. */
   private static class CallCount {
-    long mLowerBound;
-    long mUpperBound;
+    private long mLowerBound;
+    private long mUpperBound;
 
-    private CallCount(long lowerBound, long upperBound) {
+    public CallCount(long lowerBound, long upperBound) {
       mLowerBound = lowerBound;
       mUpperBound = upperBound;
     }
@@ -781,6 +908,21 @@
       return total >= mLowerBound && total <= mUpperBound;
     }
 
+    /** Tells us how long we should block waiting for the verify to happen. */
+    public long getTimeout() {
+      return 0;
+    }
+
+    public CallCount setLowerBound(long lowerBound) {
+      mLowerBound = lowerBound;
+      return this;
+    }
+
+    public CallCount setUpperBound(long upperBound) {
+      mUpperBound = upperBound;
+      return this;
+    }
+
     @Override
     public String toString() {
       if (mLowerBound == mUpperBound) {
@@ -792,6 +934,26 @@
     }
   }
 
+  /** Encapsulates adding number of times behaviour to a call count with timeout. */
+  public static final class Timeout extends CallCount {
+    private long mTimeoutMillis;
+
+    public Timeout(long lowerBound, long upperBound, long timeoutMillis) {
+      super(lowerBound, upperBound);
+      mTimeoutMillis = timeoutMillis;
+    }
+
+    @Override
+    public long getTimeout() {
+      return mTimeoutMillis;
+    }
+
+    public CallCount times(int times) { return setLowerBound(times).setUpperBound(times); }
+    public CallCount atLeast(long n) { return setLowerBound(n).setUpperBound(Long.MAX_VALUE); }
+    public CallCount atLeastOnce() { return setLowerBound(1).setUpperBound(Long.MAX_VALUE); }
+    public CallCount between(long n, long m) { return setLowerBound(n).setUpperBound(m); }
+  }
+
   /** Helper method to add an 's' to a string iff the count is not 1. */
   private static String plural(String prefix, long count) {
     return (count == 1) ? prefix : (prefix + "s");
@@ -861,12 +1023,6 @@
     return (DefaultInvocationHandler) invocationHandler;
   }
 
-  /** Builds a dynamic proxy that implements the given interface, delegating to given handler. */
-  @SuppressWarnings("unchecked")
-  private static <T> T newProxy(Class<?> theInterface, InvocationHandler theHandler) {
-    return (T) Proxy.newProxyInstance(getClassLoader(), new Class<?>[]{ theInterface }, theHandler);
-  }
-
   /** Gets a suitable class loader for use with the proxy. */
   private static ClassLoader getClassLoader() {
     return LittleMock.class.getClassLoader();
@@ -878,4 +1034,32 @@
     field.set(object, value);
     field.setAccessible(false);
   }
+
+  /** Helper method to throw an IllegalStateException if given condition is not met. */
+  private static void checkState(boolean condition, String message) {
+    if (!condition) {
+      throw new IllegalStateException(message);
+    }
+  }
+
+  /** Create a dynamic proxy for the given class, delegating to the given invocation handler. */
+  private static Object createProxy(Class<?> clazz, InvocationHandler handler) {
+    if (clazz.isInterface()) {
+      return Proxy.newProxyInstance(getClassLoader(), new Class<?>[] { clazz }, handler);
+    }
+    try {
+      Class<?> proxyBuilder = Class.forName("com.google.dexmaker.stock.ProxyBuilder");
+      Method forClassMethod = proxyBuilder.getMethod("forClass", Class.class);
+      Object builder = forClassMethod.invoke(null, clazz);
+      Method handlerMethod = builder.getClass().getMethod("handler", InvocationHandler.class);
+      builder = handlerMethod.invoke(builder, handler);
+      Method dexCacheMethod = builder.getClass().getMethod("dexCache", File.class);
+      File directory = AppDataDirGuesser.getsInstance().guessSuitableDirectoryForGeneratedClasses();
+      builder = dexCacheMethod.invoke(builder, directory);
+      Method buildMethod = builder.getClass().getMethod("build");
+      return buildMethod.invoke(builder);
+    } catch (Exception e) {
+      throw new IllegalStateException("Could not mock this concrete class", e);
+    }
+  }
 }
diff --git a/tests/com/google/testing/littlemock/AppDataDirGuesserTest.java b/tests/com/google/testing/littlemock/AppDataDirGuesserTest.java
new file mode 100644
index 0000000..b537097
--- /dev/null
+++ b/tests/com/google/testing/littlemock/AppDataDirGuesserTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.testing.littlemock;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class AppDataDirGuesserTest extends TestCase {
+  public void testGuessCacheDir_SimpleExample() {
+    guessCacheDirFor("/data/app/a.b.c.apk").shouldGive("/data/data/a.b.c/cache");
+    guessCacheDirFor("/data/app/a.b.c.tests.apk").shouldGive("/data/data/a.b.c.tests/cache");
+  }
+
+  public void testGuessCacheDir_MultipleResultsSeparatedByColon() {
+    guessCacheDirFor("/data/app/a.b.c.apk:/data/app/d.e.f.apk")
+        .shouldGive("/data/data/a.b.c/cache", "/data/data/d.e.f/cache");
+  }
+
+  public void testGuessCacheDir_NotWriteableSkipped() {
+    guessCacheDirFor("/data/app/a.b.c.apk:/data/app/d.e.f.apk")
+        .withNonWriteable("/data/data/a.b.c/cache")
+        .shouldGive("/data/data/d.e.f/cache");
+  }
+
+  public void testGuessCacheDir_StripHyphenatedSuffixes() {
+    guessCacheDirFor("/data/app/a.b.c-2.apk").shouldGive("/data/data/a.b.c/cache");
+  }
+
+  public void testGuessCacheDir_LeadingAndTrailingColonsIgnored() {
+    guessCacheDirFor("/data/app/a.b.c.apk:asdf:").shouldGive("/data/data/a.b.c/cache");
+    guessCacheDirFor(":asdf:/data/app/a.b.c.apk").shouldGive("/data/data/a.b.c/cache");
+  }
+
+  public void testGuessCacheDir_InvalidInputsGiveEmptyArray() {
+    guessCacheDirFor("").shouldGive();
+  }
+
+  public void testGuessCacheDir_JarsIgnored() {
+    guessCacheDirFor("/data/app/a.b.c.jar").shouldGive();
+    guessCacheDirFor("/system/framework/android.test.runner.jar").shouldGive();
+  }
+
+  public void testGuessCacheDir_RealWorldExample() {
+    String realPath = "/system/framework/android.test.runner.jar:" +
+        "/data/app/com.google.android.voicesearch.tests-2.apk:" +
+        "/data/app/com.google.android.voicesearch-1.apk";
+    guessCacheDirFor(realPath)
+        .withNonWriteable("/data/data/com.google.android.voicesearch.tests/cache")
+        .shouldGive("/data/data/com.google.android.voicesearch/cache");
+  }
+
+  private interface TestCondition {
+    TestCondition withNonWriteable(String... files);
+    void shouldGive(String... files);
+  }
+
+  private TestCondition guessCacheDirFor(final String path) {
+    final Set<String> notWriteable = new HashSet<String>();
+    return new TestCondition() {
+      @Override
+      public void shouldGive(String... files) {
+        AppDataDirGuesser guesser = new AppDataDirGuesser() {
+          @Override
+          public boolean isWriteableDirectory(File file) {
+            return !notWriteable.contains(file.getAbsolutePath());
+          }
+        };
+        File[] results = guesser.guessPath(path);
+        assertNotNull("Null results for " + path, results);
+        assertEquals("Bad lengths for " + path, files.length, results.length);
+        for (int i = 0; i < files.length; ++i) {
+          assertEquals("Element " + i, new File(files[i]), results[i]);
+        }
+      }
+
+      @Override
+      public TestCondition withNonWriteable(String... files) {
+        notWriteable.addAll(Arrays.asList(files));
+        return this;
+      }
+    };
+  }
+}
diff --git a/tests/com/google/testing/littlemock/LittleMockTest.java b/tests/com/google/testing/littlemock/LittleMockTest.java
index 9d3cf38..e8c7b1e 100644
--- a/tests/com/google/testing/littlemock/LittleMockTest.java
+++ b/tests/com/google/testing/littlemock/LittleMockTest.java
@@ -42,6 +42,7 @@
 import static com.google.testing.littlemock.LittleMock.mock;
 import static com.google.testing.littlemock.LittleMock.never;
 import static com.google.testing.littlemock.LittleMock.reset;
+import static com.google.testing.littlemock.LittleMock.timeout;
 import static com.google.testing.littlemock.LittleMock.times;
 import static com.google.testing.littlemock.LittleMock.verify;
 import static com.google.testing.littlemock.LittleMock.verifyNoMoreInteractions;
@@ -50,16 +51,18 @@
 import junit.framework.TestCase;
 
 import java.io.IOException;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 /**
  * Unit tests for the LittleMock class.
@@ -67,16 +70,6 @@
  * @author hugohudson@gmail.com (Hugo Hudson)
  */
 public class LittleMockTest extends TestCase {
-  /**
-   * Used in these unit tests to indicate that the method should throw a given type of exception.
-   */
-  @Target({ ElementType.METHOD })
-  @Retention(RetentionPolicy.RUNTIME)
-  public @interface ShouldThrow {
-    public Class<? extends Throwable> value();
-    public String[] messages() default {};
-  }
-
   @Mock private Foo mFoo;
   @Mock private Bar mBar;
   @Mock private BarSubtype mBarSubtype;
@@ -84,36 +77,20 @@
   @Captor private ArgumentCaptor<String> mCaptureAnotherString;
   @Captor private ArgumentCaptor<Integer> mCaptureInteger;
   @Captor private ArgumentCaptor<Callback> mCaptureCallback;
+  private ExecutorService mExecutorService;
 
   @Override
   protected void setUp() throws Exception {
     super.setUp();
     LittleMock.initMocks(this);
+    mExecutorService = Executors.newCachedThreadPool();
   }
 
   @Override
-  protected void runTest() throws Throwable {
-    Method method = getClass().getMethod(getName(), (Class[]) null);
-    ShouldThrow shouldThrowAnnotation = method.getAnnotation(ShouldThrow.class);
-    if (shouldThrowAnnotation != null) {
-      try {
-        super.runTest();
-        fail("Should have thrown " + shouldThrowAnnotation.value());
-      } catch (Throwable e) {
-        if (!e.getClass().equals(shouldThrowAnnotation.value())) {
-          fail("Should have thrown " + shouldThrowAnnotation.value() + " but threw " + e);
-        }
-        for (String requiredSubstring : shouldThrowAnnotation.messages()) {
-          if (!e.getMessage().contains(requiredSubstring)) {
-            fail("Error message didn't contain " + requiredSubstring + ", was " + e.getMessage());
-          }
-        }
-        // Good, test passes.
-      }
-    } else {
-      super.runTest();
+    protected void tearDown() throws Exception {
+      mExecutorService.shutdown();
+      super.tearDown();
     }
-  }
 
   /** Simple interface for testing against. */
   public interface Callback {
@@ -186,14 +163,18 @@
     assertEquals(null, mFoo.anInterface());
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testVerify_FailsIfNotDoneOnAProxy() {
-    verify("hello").contains("something");
+    try {
+      verify("hello").contains("something");
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testVerify_FailsIfNotCreatedByOurMockMethod() {
-    verify(createNotARealMock()).add("something");
+    try {
+      verify(createNotARealMock()).add("something");
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
   public void testVerify_SuccessfulVerification() {
@@ -215,22 +196,28 @@
     verify(mFoo).add("something");
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_MeansOnlyOnceSoShouldFailIfCalledTwice() {
     mFoo.add("something");
     mFoo.add("something");
-    verify(mFoo).add("something");
+    try {
+      verify(mFoo).add("something");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_FailedVerification_CalledWithWrongArgument() {
     mFoo.add("something else");
-    verify(mFoo).add("something");
+    try {
+      verify(mFoo).add("something");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_FailedVerification_WasNeverCalled() {
-    verify(mFoo).add("something");
+    try {
+      verify(mFoo).add("something");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerify_TimesTwice_Succeeds() {
@@ -239,25 +226,31 @@
     verify(mFoo, LittleMock.times(2)).add("jim");
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_TimesTwice_ButThreeTimesFails() {
     mFoo.add("jim");
     mFoo.add("jim");
     mFoo.add("jim");
-    verify(mFoo, LittleMock.times(2)).add("jim");
+    try {
+      verify(mFoo, LittleMock.times(2)).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_TimesTwice_ButOnceFails() {
     mFoo.add("jim");
-    verify(mFoo, LittleMock.times(2)).add("jim");
+    try {
+      verify(mFoo, LittleMock.times(2)).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_TimesTwice_DifferentStringsFails() {
     mFoo.add("jim");
     mFoo.add("bob");
-    verify(mFoo, LittleMock.times(2)).add("jim");
+    try {
+      verify(mFoo, LittleMock.times(2)).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerify_TimesTwice_WorksWithAnyString() {
@@ -266,18 +259,22 @@
     verify(mFoo, LittleMock.times(2)).add(anyString());
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_TimesTwice_FailsIfJustOnceWithAnyString() {
     mFoo.add("bob");
-    verify(mFoo, LittleMock.times(2)).add(anyString());
+    try {
+      verify(mFoo, LittleMock.times(2)).add(anyString());
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_TimesTwice_FailsIfThreeTimesWithAnyString() {
     mFoo.add("bob");
     mFoo.add("jim");
     mFoo.add("james");
-    verify(mFoo, LittleMock.times(2)).add(anyString());
+    try {
+      verify(mFoo, LittleMock.times(2)).add(anyString());
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerify_Never_Succeeds() {
@@ -285,10 +282,12 @@
     verify(mFoo, never()).anInt();
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_Never_FailsIfWasCalled() {
     mFoo.add("jim");
-    verify(mFoo, never()).add("jim");
+    try {
+      verify(mFoo, never()).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerify_Never_PassesIfArgumentsDontMatch() {
@@ -308,9 +307,11 @@
     verify(mFoo, atLeastOnce()).add("jim");
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_AtLeastOnce_FailsForNoCalls() {
-    verify(mFoo, atLeastOnce()).add("jim");
+    try {
+      verify(mFoo, atLeastOnce()).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerify_AtLeastThreeTimes_SuceedsForThreeCalls() {
@@ -329,11 +330,13 @@
     verify(mFoo, atLeast(3)).add("jim");
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_AtLeastThreeTimes_FailsForTwoCalls() {
     mFoo.add("jim");
     mFoo.add("jim");
-    verify(mFoo, atLeast(3)).add("jim");
+    try {
+      verify(mFoo, atLeast(3)).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerify_AtMostThreeTimes_SuceedsForThreeCalls() {
@@ -343,14 +346,16 @@
     verify(mFoo, atMost(3)).add("jim");
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_AtMostThreeTimes_FailsForFiveCalls() {
     mFoo.add("jim");
     mFoo.add("jim");
     mFoo.add("jim");
     mFoo.add("jim");
     mFoo.add("jim");
-    verify(mFoo, atMost(3)).add("jim");
+    try {
+      verify(mFoo, atMost(3)).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerify_AtMostThreeTimes_SucceedsForTwoCalls() {
@@ -377,20 +382,24 @@
     verify(mFoo, between(2, 4)).add("jim");
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_BetweenTwoAndFour_FailsForOneCall() {
     mFoo.add("jim");
-    verify(mFoo, between(2, 4)).add("jim");
+    try {
+      verify(mFoo, between(2, 4)).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerify_BetweenTwoAndFour_FailsForFiveCalls() {
     mFoo.add("jim");
     mFoo.add("jim");
     mFoo.add("jim");
     mFoo.add("jim");
     mFoo.add("jim");
-    verify(mFoo, LittleMock.between(2, 4)).add("jim");
+    try {
+      verify(mFoo, LittleMock.between(2, 4)).add("jim");
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testDoReturnWhen_SimpleReturn() {
@@ -416,14 +425,18 @@
     assertEquals(null, mFoo.get(2));
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testDoReturnWhen_CalledOnString() {
-    doReturn("first").when("hello").contains("something");
+    try {
+      doReturn("first").when("hello").contains("something");
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testDoReturnWhen_CalledOnNonMock() {
-    doReturn("first").when(createNotARealMock()).get(0);
+    try {
+      doReturn("first").when(createNotARealMock()).get(0);
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
   public void testDoReturnWhen_CanAlsoBeVerified() {
@@ -469,10 +482,12 @@
     assertEquals(null, mFoo.anInterface());
   }
 
-  @ShouldThrow(RuntimeException.class)
   public void testDoThrow_SimpleException() {
     doThrow(new RuntimeException()).when(mFoo).aDouble();
-    mFoo.aDouble();
+    try {
+      mFoo.aDouble();
+      fail();
+    } catch (RuntimeException expected) {}
   }
 
   public void testDoThrow_IfItDoesntMatchItIsntThrown() {
@@ -480,17 +495,17 @@
     mFoo.aChar();
   }
 
-  @ShouldThrow(RuntimeException.class)
   public void testDoThrow_KeepsThrowingForever() {
     doThrow(new RuntimeException()).when(mFoo).aDouble();
     try {
       mFoo.aDouble();
-      fail("Should have thrown a RuntimeException");
-    } catch (RuntimeException e) {
-      // Expected.
-    }
+      fail();
+    } catch (RuntimeException expected) {}
     // This second call should also throw a RuntimeException.
-    mFoo.aDouble();
+    try {
+      mFoo.aDouble();
+      fail();
+    } catch (RuntimeException expected) {}
   }
 
   public void testDoNothing() {
@@ -502,21 +517,25 @@
     verifyZeroInteractions(mFoo);
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerifyZeroInteractions_FailsIfSomethingHasHappened() {
     mFoo.aBoolean();
-    verifyZeroInteractions(mFoo);
+    try {
+      verifyZeroInteractions(mFoo);
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerifyZeroInteractions_HappyWithMultipleArguments() {
     verifyZeroInteractions(mFoo, mBar);
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerifyZeroInteractions_ShouldFailEvenIfOtherInteractionsWereFirstVerified() {
     mFoo.add("test");
     verify(mFoo).add("test");
-    verifyZeroInteractions(mFoo);
+    try {
+      verifyZeroInteractions(mFoo);
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerifyEq_Method() {
@@ -534,22 +553,28 @@
     verify(mBar).mixedArguments(eq(8), eq("test"));
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerifyEq_MethodFailsIfNotEqual() {
     mFoo.add("bob");
-    verify(mFoo).add(eq("jim"));
+    try {
+      verify(mFoo).add(eq("jim"));
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerifyEq_MethodFailsIfJustOneIsNotEqual() {
     mBar.twoStrings("first", "second");
-    verify(mBar).twoStrings(eq("first"), eq("third"));
+    try {
+      verify(mBar).twoStrings(eq("first"), eq("third"));
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerifyEq_MethodFailsIfSameParamsButInWrongOrder() {
     mBar.twoStrings("first", "second");
-    verify(mBar).twoStrings(eq("second"), eq("first"));
+    try {
+      verify(mBar).twoStrings(eq("second"), eq("first"));
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testCapture_SimpleCapture() {
@@ -622,14 +647,6 @@
     assertEquals(newList("whinny", "jessica"), mCaptureAnotherString.getAllValues());
   }
 
-  public static <T> List<T> newList(T... things) {
-    ArrayList<T> list = new ArrayList<T>();
-    for (T thing : things) {
-      list.add(thing);
-    }
-    return list;
-  }
-
   public void testAnyString() {
     doReturn("jim").when(mFoo).lookup(anyString());
     assertEquals("jim", mFoo.lookup("barney"));
@@ -641,10 +658,12 @@
     verify(mFoo).takesObject(anyString());
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testAnyString_ObjectValue() {
     mFoo.takesObject(new Object());
-    verify(mFoo).takesObject(anyString());
+    try {
+      verify(mFoo).takesObject(anyString());
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testAnyObject() {
@@ -696,11 +715,13 @@
     verifyZeroInteractions(mFoo);
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testReset_VerifyFailsAfterReset() {
     mFoo.aByte();
     reset(mFoo);
-    verify(mFoo).aByte();
+    try {
+      verify(mFoo).aByte();
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testCapture_BothBeforeAndAfter() {
@@ -738,7 +759,6 @@
     verify(mFoo).add("yes");
   }
 
-  @ShouldThrow(IOException.class)
   public void testDoAction_CanThrowDeclaredException() throws Exception {
     doAnswer(new Callable<Void>() {
       @Override
@@ -746,10 +766,12 @@
         throw new IOException("Problem");
       }
     }).when(mFoo).exceptionThrower();
-    mFoo.exceptionThrower();
+    try {
+      mFoo.exceptionThrower();
+      fail();
+    } catch (IOException expected) {}
   }
 
-  @ShouldThrow(RuntimeException.class)
   public void testDoAction_CanThrowUndelcaredException() {
     doAnswer(new Callable<Void>() {
       @Override
@@ -757,7 +779,10 @@
         throw new RuntimeException("Problem");
       }
     }).when(mFoo).aBoolean();
-    mFoo.aBoolean();
+    try {
+      mFoo.aBoolean();
+      fail();
+    } catch (RuntimeException expected) {}
   }
 
   public void testRunThisIsAnAliasForDoAction() {
@@ -796,10 +821,12 @@
     verifyNoMoreInteractions(mFoo, mBar);
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerifyNoMoreInteractions_FailsWhenOneActionWasNotVerified() {
     mFoo.aBoolean();
-    verifyNoMoreInteractions(mFoo, mBar);
+    try {
+      verifyNoMoreInteractions(mFoo, mBar);
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerifyNoMoreInteractions_SucceedsWhenAllActionsWereVerified() {
@@ -809,13 +836,15 @@
     verifyNoMoreInteractions(mFoo);
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testVerifyNoMoreInteractions_FailsWhenMostButNotAllActionsWereVerified() {
     mFoo.get(3);
     mFoo.get(20);
     mFoo.aByte();
     verify(mFoo, atLeastOnce()).get(anyInt());
-    verifyNoMoreInteractions(mFoo);
+    try {
+      verifyNoMoreInteractions(mFoo);
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testVerifyNoMoreInteractions_ShouldIngoreStubbedCalls() {
@@ -824,17 +853,20 @@
     verifyNoMoreInteractions(mFoo);
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testMatchers_WrongNumberOfMatchersOnStubbingCausesError() {
-    doReturn("hi").when(mBar).twoStrings("jim", eq("bob"));
+    try {
+      doReturn("hi").when(mBar).twoStrings("jim", eq("bob"));
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testMatchers_WrongNumberOfMatchersOnVerifyCausesError() {
-    verify(mBar).twoStrings("jim", eq("bob"));
+    try {
+      verify(mBar).twoStrings("jim", eq("bob"));
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
-  @ShouldThrow(IllegalStateException.class)
   public void testCreateACaptureButDontUseItShouldFailAtNextVerify() {
     // If we create a capture illegally, outside of a method call, like so:
     mCaptureString.capture();
@@ -843,23 +875,29 @@
     // call on the mock object.
     // Thus we should check in the verify() method that there are *no matchers* on the static
     // list, as this would indicate a programming error such as the above.
-    verify(mFoo, anyTimes()).aBoolean();
+    try {
+      verify(mFoo, anyTimes()).aBoolean();
+      fail();
+    } catch (IllegalStateException expected) {}
   }
 
-  @ShouldThrow(IllegalStateException.class)
   public void testCreateACaptureButDontUseItShouldFailAtNextVerify_AlsoNoMoreInteractions() {
     // Same result desired as in previous test.
     mCaptureString.capture();
-    verifyNoMoreInteractions(mFoo);
+    try {
+      verifyNoMoreInteractions(mFoo);
+      fail();
+    } catch (IllegalStateException expected) {}
   }
 
-  @ShouldThrow(IllegalStateException.class)
   public void testCreateACaptureButDontUseItShouldFailAtNextVerify_AlsoZeroInteraction() {
     mCaptureString.capture();
-    verifyZeroInteractions(mFoo);
+    try {
+      verifyZeroInteractions(mFoo);
+      fail();
+    } catch (IllegalStateException expected) {}
   }
 
-  @ShouldThrow(IllegalStateException.class)
   public void testCheckStaticVariablesMethod() {
     // To help us avoid programming errors, I'm adding a method that you can call from tear down,
     // which will explode if there is anything still left in your static variables at the end
@@ -867,12 +905,17 @@
     // variable (so that the next test won't fail).  It should fail if we create a matcher
     // be it a capture or anything else that is then not consumed.
     anyInt();
-    checkForProgrammingErrorsDuringTearDown();
+    try {
+      checkForProgrammingErrorsDuringTearDown();
+      fail();
+    } catch (IllegalStateException expected) {}
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testCantPassNullToVerifyCount() {
-    verify(mFoo, null).aBoolean();
+    try {
+      verify(mFoo, null).aBoolean();
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
   public void testInjectionInNestedClasses() throws Exception {
@@ -901,10 +944,12 @@
     verify(mFoo).takesObject(isA(String.class));
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testIsA_FailsWithSuperclass() {
     mFoo.takesObject(new Object());
-    verify(mFoo).takesObject(isA(String.class));
+    try {
+      verify(mFoo).takesObject(isA(String.class));
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testIsA_WillAcceptNull() {
@@ -918,49 +963,57 @@
     verify(mFoo).takesBar(isA(BarSubtype.class));
   }
 
-  @ShouldThrow(AssertionError.class)
   public void testIsA_MatchSubtypeFailed() {
     mFoo.takesBar(mBar);
-    verify(mFoo).takesBar(isA(BarSubtype.class));
+    try {
+      verify(mFoo).takesBar(isA(BarSubtype.class));
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(value = AssertionError.class,
-      messages = {"cannot verify call to", "boolean java.lang.Object.equals(java.lang.Object)"})
   public void testVerifyEquals_ShouldFail() {
     mFoo.equals(null);
-    verify(mFoo).equals(null);
+    try {
+      verify(mFoo).equals(null);
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(value = AssertionError.class,
-      messages = {"cannot verify call to", "int java.lang.Object.hashCode()"})
   public void testVerifyHashCode_ShouldFail() {
     mFoo.hashCode();
-    verify(mFoo).hashCode();
+    try {
+      verify(mFoo).hashCode();
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(value = AssertionError.class,
-      messages = {"cannot verify call to", "java.lang.String java.lang.Object.toString()"})
   public void testVerifyToString_ShouldFail() {
     mFoo.toString();
-    verify(mFoo).toString();
+    try {
+      verify(mFoo).toString();
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(value = AssertionError.class,
-      messages = {"cannot stub call to", "boolean java.lang.Object.equals(java.lang.Object)"})
   public void testStubEquals_ShouldFail() {
-    doReturn(false).when(mFoo).equals(null);
+    try {
+      doReturn(false).when(mFoo).equals(null);
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(value = AssertionError.class,
-      messages = {"cannot stub call to", "int java.lang.Object.hashCode()"})
   public void testStubHashCode_ShouldFail() {
-    doReturn(0).when(mFoo).hashCode();
+    try {
+      doReturn(0).when(mFoo).hashCode();
+      fail();
+    } catch (AssertionError expected) {}
   }
 
-  @ShouldThrow(value = AssertionError.class,
-      messages = {"cannot stub call to", "java.lang.String java.lang.Object.toString()"})
   public void testStubToString_ShouldFail() {
-    doReturn("party").when(mFoo).toString();
+    try {
+      doReturn("party").when(mFoo).toString();
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testEqualsMethod_DoesntCountAsInteraction() {
@@ -1014,7 +1067,7 @@
           + "  mFoo.aBoolean()\n"
           + "  at " + singleLineStackTrace(verifyLineNumber) + "\n"
           + "\n"
-          + "No method calls happened to this mock\n";
+          + "No method calls happened on this mock\n";
       assertEquals(expectedMessage, e.getMessage());
     }
   }
@@ -1100,23 +1153,29 @@
     }
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testDoReturn_ThisShouldFailSinceDoubleIsNotAString() {
-    doReturn("hello").when(mFoo).aDouble();
+    try {
+      doReturn("hello").when(mFoo).aDouble();
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
   public void testDoReturn_ThisShouldPassSinceStringCanBeReturnedFromObjectMethod() {
     doReturn("hello").when(mFoo).anObject();
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testDoReturn_ThisShouldFailSinceObjectCantBeReturnedFromString() {
-    doReturn(new Object()).when(mFoo).aString();
+    try {
+      doReturn(new Object()).when(mFoo).aString();
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testDoReturn_ThisShouldFailSinceBarIsNotSubtypeOfBarSubtype() {
-    doReturn(mBar).when(mFoo).aBarSubtype();
+    try {
+      doReturn(mBar).when(mFoo).aBarSubtype();
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
   public void testDoReturn_ThisShouldPassSinceBarSubTypeIsABar() {
@@ -1132,10 +1191,12 @@
   // TODO(hugohudson): 7. Should fix this.
   // Once we fix the previous test we won't need this one.
   // I'm just demonstrating that currently this fails with NPE at use-time not at stub-time.
-  @ShouldThrow(NullPointerException.class)
   public void testDoReturn_ThisShouldFailBecauseNullIsNotAByte2() {
     doReturn(null).when(mFoo).aByte();
-    mFoo.aByte();
+    try {
+      mFoo.aByte();
+      fail();
+    } catch (NullPointerException expected) {}
   }
 
   public void testDoReturn_ThisShouldPassSinceNullIsAnObject() {
@@ -1155,30 +1216,36 @@
   // TODO(hugohudson): 7. Should fix this to give proper message.
   // We could at least give a good message saying why you get failure - saying that your string
   // is not a byte, and pointing to where you stubbed it.
-  @ShouldThrow(ClassCastException.class)
   public void testDoAnswer_ThisShouldFailSinceStringIsNotAByte2() {
     doAnswer(new Callable<String>() {
       @Override public String call() throws Exception { return "hi"; }
     }).when(mFoo).aByte();
-    mFoo.aByte();
+    try {
+      mFoo.aByte();
+      fail();
+    } catch (ClassCastException expected) {}
   }
 
-  @ShouldThrow(value = IllegalArgumentException.class,
-      messages = { "  (Bar) mFoo.aBar()" })
   public void testDoAnswer_ShouldHaveSimpleNameOnReturnValue() {
-    doReturn("hi").when(mFoo).aBar();
+    try {
+      doReturn("hi").when(mFoo).aBar();
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
-  @ShouldThrow(IllegalArgumentException.class)
   public void testCantCreateMockOfNullType() {
-    mock(null);
+    try {
+      mock(null);
+      fail();
+    } catch (IllegalArgumentException expected) {}
   }
 
-  @ShouldThrow(value = AssertionError.class,
-      messages = { "Expected exactly 1 call to:", "onClickListener.onClick(Bar)" })
   public void testCreateMockWithNullFieldName() {
     OnClickListener mockClickListener = mock(OnClickListener.class);
-    verify(mockClickListener).onClick(null);
+    try {
+      verify(mockClickListener).onClick(null);
+      fail();
+    } catch (AssertionError expected) {}
   }
 
   public void testDoubleVerifyNoProblems() {
@@ -1191,10 +1258,185 @@
     verify(mFoo).aByte();
   }
 
-  // TODO(hugohudson): 5. Every @ShouldThrow method should be improved by adding test for the
-  // content of the error message.  First augment the annotation to take a String which must
-  // form a substring of the getMessage() for the Exception, then check that the exceptions
-  // thrown are as informative as possible.
+  public void testUnfinishedVerifyCaughtInTearDown_Issue5() {
+    verify(mFoo);
+    try {
+      checkForProgrammingErrorsDuringTearDown();
+      fail();
+    } catch (IllegalStateException expected) {}
+  }
+
+  public void testUnfinishedWhenCaughtInTearDown_Issue5() {
+    doThrow(new RuntimeException()).when(mFoo);
+    try {
+      checkForProgrammingErrorsDuringTearDown();
+      fail();
+    } catch (IllegalStateException expected) {}
+  }
+
+  public void testUnfinishedVerifyCaughtByStartingWhen_Issue5() {
+    verify(mFoo, never());
+    try {
+      doReturn(null).when(mFoo).clear();
+      fail();
+    } catch (IllegalStateException expected) {}
+  }
+
+  public void testUnfinishedWhenCaughtByStartingVerify_Issue5() {
+    doThrow(new RuntimeException()).when(mFoo);
+    try {
+      verify(mFoo).clear();
+      fail();
+    } catch (IllegalStateException expected) {}
+  }
+
+  public void testSecondUnfinishedVerifyShouldFailImmediately() throws Exception {
+    verify(mFoo);
+    try {
+      verify(mFoo);
+      fail();
+    } catch (IllegalStateException expected) {}
+  }
+
+  public void testSecondUnfinishedWhenShouldFailImmediately() throws Exception {
+    doReturn(null).when(mFoo);
+    try {
+      doReturn(null).when(mFoo);
+      fail();
+    } catch (IllegalStateException expected) {}
+  }
+
+  public void testVerifyWithTimeout_SuccessCase() throws Exception {
+    CountDownLatch countDownLatch = new CountDownLatch(1);
+    invokeBarMethodAfterLatchAwait(countDownLatch);
+    doReturn(null).when(mFoo).aBar();
+    verify(mFoo, never()).aBar();
+    countDownLatch.countDown();
+    verify(mFoo, timeout(100)).aBar();
+  }
+
+  public void testVerifyWithTimeout_FailureCase() throws Exception {
+    long start = System.currentTimeMillis();
+    try {
+      verify(mFoo, timeout(10)).aBar();
+      fail();
+    } catch (AssertionError expected) {}
+    long duration = System.currentTimeMillis() - start;
+    assertTrue(duration > 5);
+  }
+
+  public void testVerifyWithTimeoutMultipleInvocations_SuccessCase() throws Exception {
+    CountDownLatch countDownLatch = new CountDownLatch(1);
+    invokeBarMethodAfterLatchAwait(countDownLatch);
+    invokeBarMethodAfterLatchAwait(countDownLatch);
+    doReturn(null).when(mFoo).aBar();
+    verify(mFoo, never()).aBar();
+    countDownLatch.countDown();
+    verify(mFoo, timeout(100).times(2)).aBar();
+    verify(mFoo, timeout(100).atLeast(2)).aBar();
+    verify(mFoo, timeout(100).between(2, 4)).aBar();
+    verify(mFoo, timeout(100).atLeastOnce()).aBar();
+  }
+
+  public void testVerifyWithTimeoutMultipleInvocations_FailureCase() throws Exception {
+    long start = System.currentTimeMillis();
+    mFoo.aBar();
+    try {
+      verify(mFoo, timeout(10).between(2, 3)).aBar();
+      fail();
+    } catch (AssertionError expected) {}
+    long duration = System.currentTimeMillis() - start;
+    assertTrue(duration > 5);
+
+  }
+
+  public void testConcurrentModificationExceptions() throws Exception {
+    // This test concurrently stubs, verifies, and uses a mock.
+    // It used to totally reproducibly throw a ConcurrentModificationException.
+    Future<?> task1 = mExecutorService.submit(new Runnable() {
+      @Override
+      public void run() {
+        while (!Thread.currentThread().isInterrupted()) {
+          mFoo.aBar();
+          Thread.yield();
+        }
+      }
+    });
+    Future<?> task2 = mExecutorService.submit(new Runnable() {
+        @Override
+        public void run() {
+          while (!Thread.currentThread().isInterrupted()) {
+            verify(mFoo, anyTimes()).aBar();
+            Thread.yield();
+          }
+        }
+      });
+    Thread.sleep(20);
+    task1.cancel(true);
+    task2.cancel(true);
+    try {
+      task1.get();
+      fail();
+    } catch (CancellationException expected) {}
+    try {
+      task2.get();
+      fail();
+    } catch (CancellationException expected) {}
+  }
+
+  public void testCannotVerifyFromSecondThreadAfterStubbingInFirst() throws Exception {
+    doReturn(null).when(mFoo).aBar();
+    Future<?> submit = mExecutorService.submit(new Runnable() {
+      @Override
+      public void run() {
+        verify(mFoo, anyTimes()).aBar();
+      }
+    });
+    try {
+      submit.get();
+      fail();
+    } catch (ExecutionException expected) {
+      assertTrue(expected.getCause() instanceof IllegalStateException);
+    }
+  }
+
+  public void testCannotStubFromSecondThreadAfterVerifyingInFirst() throws Exception {
+    mExecutorService.submit(new Runnable() {
+      @Override
+      public void run() {
+        verify(mFoo, anyTimes()).aBar();
+      }
+    }).get();
+    try {
+      doReturn(null).when(mFoo).aBar();
+      fail();
+    } catch (IllegalStateException expected) {}
+  }
+
+  public class Jim {
+    public void bob() {
+      fail();
+    }
+  }
+
+  public void testMockingConcreteClasses() throws Exception {
+    Jim mock = mock(Jim.class);
+    mock.bob();
+  }
+
+  private Future<Void> invokeBarMethodAfterLatchAwait(final CountDownLatch countDownLatch) {
+    return mExecutorService.submit(new Callable<Void>() {
+      @Override
+      public Void call() throws Exception {
+        countDownLatch.await();
+        mFoo.aBar();
+        return null;
+      }
+    });
+  }
+
+  // TODO(hugohudson): 5. Every method that throws exceptions could be improved by adding
+  // test for the content of the error message.
 
   // TODO(hugohudson): 5. Add InOrder class, so that we can check that the given methods on
   // the given mocks happen in the right order.  It will be pretty easy to do.  The syntax
@@ -1258,14 +1500,8 @@
   //  //then
   //  assertThat(goods, containBread());
 
-  // TODO(hugohudson): 6. How about timeouts that are mentioned in Mockito docs?
-  // Sounds like a good idea.  Would look like this:
-  // verify(mFoo, within(50).milliseconds()).get(0);
-
-  // TODO(hugohudson): 6. Consider bringing back in the async code I wrote for the AndroidMock
-  // framework so that you can expect to wait for the method call.
-  // Although obviously we're more keen on encouraging people to write unit tests that don't
-  // require async behaviour in the first place.
+  // TODO: All unfinished verify and when statements should have sensible error messages telling
+  // you where the unfinished statement comes from.
 
   /** Returns the line number of the line following the method call. */
   private int getNextLineNumber() {
@@ -1307,4 +1543,12 @@
       }
     };
   }
+
+  private static <T> List<T> newList(T... things) {
+    ArrayList<T> list = new ArrayList<T>();
+    for (T thing : things) {
+      list.add(thing);
+    }
+    return list;
+  }
 }