Merge remote-tracking branch 'goog/ics-aah-exp'
diff --git a/src/com/google/testing/littlemock/LittleMock.java b/src/com/google/testing/littlemock/LittleMock.java
index fc6c527..9d756dc 100644
--- a/src/com/google/testing/littlemock/LittleMock.java
+++ b/src/com/google/testing/littlemock/LittleMock.java
@@ -30,6 +30,7 @@
 import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -146,14 +147,48 @@
 
   /** Begins a verification step on a mock: the next method invocation on that mock will verify. */
   public static <T> T verify(T mock, CallCount howManyTimes) {
+    return verify(mock, howManyTimes, null);
+  }
+
+  private static final class OrderChecker {
+    private MethodCall mLastCall;
+
+    public void checkOrder(List<MethodCall> calls, String fieldName) {
+      MethodCall lastTrial = null;
+      for (MethodCall trial : calls) {
+        if (mLastCall == null || mLastCall.mInvocationOrder < trial.mInvocationOrder) {
+          mLastCall = trial;
+          return;
+        }
+        lastTrial = trial;
+      }
+      fail(formatFailedVerifyOrderMessage(mLastCall, lastTrial, fieldName));
+    }
+
+    private String formatFailedVerifyOrderMessage(MethodCall lastCall, MethodCall thisCall,
+        String fieldName) {
+      StringBuffer sb = new StringBuffer();
+      sb.append("\nCall to:");
+      appendDebugStringForMethodCall(sb, thisCall.mMethod, thisCall.mElement, fieldName, false);
+      sb.append("\nShould have happened after:");
+      appendDebugStringForMethodCall(sb, lastCall.mMethod, lastCall.mElement, fieldName, false);
+      sb.append("\nBut the calls happened in the wrong order");
+      sb.append("\n");
+      return sb.toString();
+    }
+  }
+
+  private static <T> T verify(T mock, CallCount howManyTimes, OrderChecker orderCounter) {
     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.mOrderCounter == null, "Unfinished verify() statements");
     checkState(handler.mStubbingAction == null, "Unfinished stubbing statements");
     checkNoMatchers();
     handler.mHowManyTimes = howManyTimes;
+    handler.mOrderCounter = orderCounter;
     sUnfinishedCallCounts.add(howManyTimes);
     return handler.<T>getVerifyingMock();
   }
@@ -273,7 +308,7 @@
     return addMatcher(new ArgumentMatcher() {
       @Override
       public boolean matches(Object value) {
-        return (expected == null) ? (value == null) : expected.equals(value);
+        return areEqual(expected, value);
       }
     }, expected);
   }
@@ -422,6 +457,7 @@
     public <T> T when(T mock) {
       DefaultInvocationHandler handler = getHandlerFrom(mock);
       checkState(handler.mHowManyTimes == null, "Unfinished verify() statements");
+      checkState(handler.mOrderCounter == null, "Unfinished verify() statements");
       checkState(handler.mStubbingAction == null, "Unfinished stubbing statements");
       handler.mStubbingAction = mAction;
       sUnfinishedStubbingActions.add(mAction);
@@ -450,12 +486,17 @@
    */
   private static final List<ArgumentMatcher> sMatchArguments = new ArrayList<ArgumentMatcher>();
 
+  /** Global invocation order of every mock method call. */
+  private static final AtomicLong sGlobalInvocationOrder = new AtomicLong();
+
   /** Encapsulates a single call of a method with associated arguments. */
   private static class MethodCall {
     /** The method call. */
     private final Method mMethod;
     /** The arguments provided at the time the call happened. */
     private final Object[] mArgs;
+    /** The order in which this method call was invoked. */
+    private final long mInvocationOrder;
     /** The line from the test that invoked the handler to create this method call. */
     private final StackTraceElement mElement;
     /** Keeps track of method calls that have been verified, for verifyNoMoreInteractions(). */
@@ -465,6 +506,7 @@
       mMethod = method;
       mElement = element;
       mArgs = args;
+      mInvocationOrder = sGlobalInvocationOrder.getAndIncrement();
     }
 
     public boolean argsMatch(Object[] args) {
@@ -477,6 +519,20 @@
     }
   }
 
+  private static boolean areMethodsSame(Method first, Method second) {
+    return areEqual(first.getDeclaringClass(), second.getDeclaringClass()) &&
+        areEqual(first.getName(), second.getName()) &&
+        areEqual(first.getReturnType(), second.getReturnType()) &&
+        Arrays.equals(first.getParameterTypes(), second.getParameterTypes());
+  }
+
+  private static boolean areEqual(Object a, Object b) {
+    if (a == null) {
+      return b == null;
+    }
+    return a.equals(b);
+  }
+
   /**
    * Magically handles the invoking of method calls.
    *
@@ -523,6 +579,7 @@
      * <p>It is reset to null once the verification has occurred.
      */
     private CallCount mHowManyTimes = null;
+    private OrderChecker mOrderCounter = null;
 
     /**
      * The action to be associated with the stubbed method.
@@ -591,11 +648,12 @@
             ArgumentMatcher[] matchers = checkClearAndGetMatchers(method);
             StackTraceElement callSite = new Exception().getStackTrace()[2];
             MethodCall methodCall = new MethodCall(method, callSite, args);
-            innerVerify(method, matchers, methodCall, proxy, callSite, mHowManyTimes);
+            innerVerify(method, matchers, methodCall, proxy, callSite, mHowManyTimes, mOrderCounter);
             return defaultReturnValue(method.getReturnType());
           } finally {
             sUnfinishedCallCounts.remove(mHowManyTimes);
             mHowManyTimes = null;
+            mOrderCounter = null;
           }
         }
       });
@@ -639,9 +697,9 @@
      * @param operation the name of the operation, used for generating a helpful message
      */
     private void checkSpecialObjectMethods(Method method, String operation) {
-      if (method.equals(sEqualsMethod)
-          || method.equals(sHashCodeMethod)
-          || method.equals(sToStringMethod)) {
+      if (areMethodsSame(method, sEqualsMethod)
+          || areMethodsSame(method, sHashCodeMethod)
+          || areMethodsSame(method, sToStringMethod)) {
         fail("cannot " + operation + " call to " + method);
       }
     }
@@ -650,24 +708,25 @@
       mRecordedCalls.clear();
       mStubbedCalls.clear();
       mHowManyTimes = null;
+      mOrderCounter = null;
       mStubbingAction = null;
     }
 
     private Object innerRecord(Method method, final Object[] args,
             MethodCall methodCall, Object proxy, StackTraceElement callSite) throws Throwable {
-      if (method.equals(sEqualsMethod)) {
+      if (areMethodsSame(method, sEqualsMethod)) {
         // Use identify for equality, the default behavior on object.
         return proxy == args[0];
-      } else if (method.equals(sHashCodeMethod)) {
+      } else if (areMethodsSame(method, sHashCodeMethod)) {
         // This depends on the fact that each mock has its own DefaultInvocationHandler.
         return hashCode();
-      } else if (method.equals(sToStringMethod)) {
+      } else if (areMethodsSame(method, sToStringMethod)) {
         // This is used to identify this is a mock, e.g., in error messages.
         return "Mock<" + mClazz.getName() + ">";
       }
       mRecordedCalls.add(methodCall);
       for (StubbedCall stubbedCall : mStubbedCalls) {
-        if (stubbedCall.mMethodCall.mMethod.equals(methodCall.mMethod)) {
+        if (areMethodsSame(stubbedCall.mMethodCall.mMethod, methodCall.mMethod)) {
           if (stubbedCall.mMethodCall.argsMatch(methodCall.mArgs)) {
             methodCall.mWasVerified = true;
             return stubbedCall.mAction.doAction(method, args);
@@ -744,44 +803,47 @@
     }
 
     private void innerVerify(Method method, ArgumentMatcher[] matchers, MethodCall methodCall,
-        Object proxy, StackTraceElement callSite, CallCount callCount) {
+        Object proxy, StackTraceElement callSite, CallCount callCount, OrderChecker orderCounter) {
       checkSpecialObjectMethods(method, "verify");
-      int total = countMatchingInvocations(method, matchers, methodCall);
+      List<MethodCall> calls = countMatchingInvocations(method, matchers, methodCall);
       long callTimeout = callCount.getTimeout();
+      checkState(orderCounter == null || callTimeout == 0, "can't inorder verify with a timeout");
       if (callTimeout > 0) {
         long endTime = System.currentTimeMillis() + callTimeout;
-        while (!callCount.matches(total)) {
+        while (!callCount.matches(calls.size())) {
           try {
             Thread.sleep(1);
           } catch (InterruptedException e) {
             fail("interrupted whilst waiting to verify");
           }
           if (System.currentTimeMillis() > endTime) {
-            fail(formatFailedVerifyMessage(methodCall, total, callTimeout, callCount));
+            fail(formatFailedVerifyMessage(methodCall, calls.size(), callTimeout, callCount));
           }
-          total = countMatchingInvocations(method, matchers, methodCall);
+          calls = countMatchingInvocations(method, matchers, methodCall);
         }
       } else {
-        if (!callCount.matches(total)) {
-          fail(formatFailedVerifyMessage(methodCall, total, 0, callCount));
+        if (orderCounter != null) {
+            orderCounter.checkOrder(calls, mFieldName);
+        } else if (!callCount.matches(calls.size())) {
+          fail(formatFailedVerifyMessage(methodCall, calls.size(), 0, callCount));
         }
       }
     }
 
-    private int countMatchingInvocations(Method method, ArgumentMatcher[] matchers,
+    private List<MethodCall> countMatchingInvocations(Method method, ArgumentMatcher[] matchers,
         MethodCall methodCall) {
-      int total = 0;
+      List<MethodCall> methodCalls = new ArrayList<MethodCall>();
       for (MethodCall call : mRecordedCalls) {
-        if (call.mMethod.equals(method)) {
+        if (areMethodsSame(call.mMethod, method)) {
           if ((matchers.length > 0 && doMatchersMatch(matchers, call.mArgs)) ||
               call.argsMatch(methodCall.mArgs)) {
             setCaptures(matchers, call.mArgs);
-            ++total;
+            methodCalls.add(call);
             call.mWasVerified  = true;
           }
         }
       }
-      return total;
+      return methodCalls;
     }
 
     private String formatFailedVerifyMessage(MethodCall methodCall, int total, long timeoutMillis,
@@ -884,7 +946,7 @@
   }
 
   /** Represents something capable of testing if it matches an argument or not. */
-  /*package*/ interface ArgumentMatcher {
+  public interface ArgumentMatcher {
     public boolean matches(Object value);
   }
 
@@ -966,6 +1028,12 @@
     return value;
   }
 
+  /** A custom argument matcher, should be used only for object arguments not primitives. */
+  public static <T> T matches(ArgumentMatcher argument) {
+    sMatchArguments.add(argument);
+    return null;
+  }
+
   /** Utility method to throw an AssertionError if an assertion fails. */
   private static void expect(boolean result, String message) {
     if (!result) {
@@ -1031,6 +1099,13 @@
     }
   }
 
+  /** Helper method to throw an IllegalStateException if given condition is not met. */
+  private static void checkState(boolean condition) {
+    if (!condition) {
+      throw new IllegalStateException();
+    }
+  }
+
   /**
    * If the input object is one of our mocks, returns the {@link DefaultInvocationHandler}
    * we constructed it with.  Otherwise fails with {@link IllegalArgumentException}.
@@ -1050,14 +1125,92 @@
         return (DefaultInvocationHandler) invocationHandler;
       }
     } catch (Exception expectedIfNotAProxyBuilderMock) {}
+    try {
+      // Try with javassist.
+      Class<?> proxyObjectClass = Class.forName("javassist.util.proxy.ProxyObject");
+      Method getHandlerMethod = proxyObjectClass.getMethod("getHandler");
+      Object methodHandler = getHandlerMethod.invoke(mock);
+      InvocationHandler invocationHandler = Proxy.getInvocationHandler(methodHandler);
+      Method getOriginalMethod = invocationHandler.getClass().getMethod("$$getOriginal");
+      Object original = getOriginalMethod.invoke(invocationHandler);
+      if (original instanceof DefaultInvocationHandler) {
+        return (DefaultInvocationHandler) original;
+      }
+    } catch (Exception expectedIfNotJavassistProxy) {}
     throw new IllegalArgumentException("not a valid mock: " + mock);
   }
 
   /** Create a dynamic proxy for the given class, delegating to the given invocation handler. */
-  private static Object createProxy(Class<?> clazz, InvocationHandler handler) {
+  private static Object createProxy(Class<?> clazz, final InvocationHandler handler) {
+    // Interfaces are simple.  Just proxy them using java.lang.reflect.Proxy.
     if (clazz.isInterface()) {
       return Proxy.newProxyInstance(getClassLoader(), new Class<?>[] { clazz }, handler);
     }
+    // Try with javassist.
+    try {
+      Class<?> proxyFactoryClass = Class.forName("javassist.util.proxy.ProxyFactory");
+      Object proxyFactory = proxyFactoryClass.newInstance();
+      Method setSuperclassMethod = proxyFactoryClass.getMethod("setSuperclass", Class.class);
+      setSuperclassMethod.invoke(proxyFactory, clazz);
+      Class<?> methodFilterClass = Class.forName("javassist.util.proxy.MethodFilter");
+      InvocationHandler methodFilterHandler = new InvocationHandler() {
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+          checkState(method.getName().equals("isHandled"));
+          checkState(args.length == 1);
+          checkState(args[0] instanceof Method);
+          Method invokedMethod = (Method) args[0];
+          String methodName = invokedMethod.getName();
+          Class<?>[] params = invokedMethod.getParameterTypes();
+          if ("equals".equals(methodName) && params.length == 1
+              && Object.class.equals(params[0])) {
+            return false;
+          }
+          if ("hashCode".equals(methodName) && params.length == 0) {
+              return false;
+          }
+          if ("toString".equals(methodName) && params.length == 0) {
+              return false;
+          }
+          if ("finalize".equals(methodName) && params.length == 0) {
+              return false;
+          }
+          return true;
+        }
+      };
+      Object methodFilter = Proxy.newProxyInstance(getClassLoader(),
+          new Class<?>[] { methodFilterClass }, methodFilterHandler);
+      Method setFilterMethod = proxyFactoryClass.getMethod("setFilter", methodFilterClass);
+      setFilterMethod.invoke(proxyFactory, methodFilter);
+      Method createClassMethod = proxyFactoryClass.getMethod("createClass");
+      Class<?> createdClass = (Class<?>) createClassMethod.invoke(proxyFactory);
+      InvocationHandler methodHandlerHandler = new InvocationHandler() {
+        @SuppressWarnings("unused")
+        public InvocationHandler $$getOriginal() {
+          return handler;
+        }
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+          checkState(method.getName().equals("invoke"));
+          checkState(args.length == 4);
+          checkState(args[1] instanceof Method);
+          Method invokedMethod = (Method) args[1];
+          checkState(args[3] instanceof Object[]);
+          return handler.invoke(args[0], invokedMethod, (Object[]) args[3]);
+        }
+      };
+      Class<?> methodHandlerClass = Class.forName("javassist.util.proxy.MethodHandler");
+      Object methodHandler = Proxy.newProxyInstance(getClassLoader(),
+          new Class<?>[] { methodHandlerClass }, methodHandlerHandler);
+      Object proxy = unsafeCreateInstance(createdClass);
+      Class<?> proxyObjectClass = Class.forName("javassist.util.proxy.ProxyObject");
+      Method setHandlerMethod = proxyObjectClass.getMethod("setHandler", methodHandlerClass);
+      setHandlerMethod.invoke(proxy, methodHandler);
+      return proxy;
+    } catch (Exception e) {
+      // Not supported, i.e. javassist missing.  Fall through.
+    }
+    // So, this is a class.  First try using Android's ProxyBuilder from dexmaker.
     try {
       Class<?> proxyBuilder = Class.forName("com.google.dexmaker.stock.ProxyBuilder");
       Method forClassMethod = proxyBuilder.getMethod("forClass", Class.class);
@@ -1080,6 +1233,15 @@
   /** Attempt to construct an instance of the class using hacky methods to avoid calling super. */
   @SuppressWarnings("unchecked")
   private static <T> T unsafeCreateInstance(Class<T> clazz) {
+    // try jvm
+    try {
+      Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+      Field f = unsafeClass.getDeclaredField("theUnsafe");
+      f.setAccessible(true);
+      final Object unsafe = f.get(null);
+      final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
+      return (T) allocateInstance.invoke(unsafe, clazz);
+    } catch (Exception ignored) {}
     // try dalvikvm, pre-gingerbread
     try {
       final Method newInstance = ObjectInputStream.class.getDeclaredMethod(
@@ -1100,4 +1262,31 @@
     } catch (Exception ignored) {}
     throw new IllegalStateException("unsafe create instance failed");
   }
+
+  /** See {@link LittleMock#inOrder(Object[])}. */
+  public interface InOrder {
+      <T> T verify(T mock);
+  }
+
+  /**
+   * Used to verify that invocations happen in a given order.
+   * <p>
+   * Still slight experimental at the moment: you can only verify one method call at a time,
+   * and we ignore the mocks array you pass in as an argument, you may use the returned inorder
+   * to verify all mocks.
+   * <p>
+   * This implementation is simple: the InOrder you get from this call can be used to verify that
+   * a sequence of method calls happened in the order you specify.  Every verify you make with
+   * this InOrder will be compared with every other verify you made, to make sure that all the
+   * original invocations happened in exactly that same order.
+   */
+  public static InOrder inOrder(Object... mocks) {
+    return new InOrder() {
+      private final OrderChecker mChecker = new OrderChecker();
+      @Override
+      public <T> T verify(T mock) {
+        return LittleMock.verify(mock, times(1), mChecker);
+      }
+    };
+  }
 }
diff --git a/tests/com/google/testing/littlemock/LittleMockTest.java b/tests/com/google/testing/littlemock/LittleMockTest.java
index fde8c7c..e7f9bca 100644
--- a/tests/com/google/testing/littlemock/LittleMockTest.java
+++ b/tests/com/google/testing/littlemock/LittleMockTest.java
@@ -37,8 +37,10 @@
 import static com.google.testing.littlemock.LittleMock.doReturn;
 import static com.google.testing.littlemock.LittleMock.doThrow;
 import static com.google.testing.littlemock.LittleMock.eq;
+import static com.google.testing.littlemock.LittleMock.inOrder;
 import static com.google.testing.littlemock.LittleMock.initMocks;
 import static com.google.testing.littlemock.LittleMock.isA;
+import static com.google.testing.littlemock.LittleMock.matches;
 import static com.google.testing.littlemock.LittleMock.mock;
 import static com.google.testing.littlemock.LittleMock.never;
 import static com.google.testing.littlemock.LittleMock.reset;
@@ -48,6 +50,9 @@
 import static com.google.testing.littlemock.LittleMock.verifyNoMoreInteractions;
 import static com.google.testing.littlemock.LittleMock.verifyZeroInteractions;
 
+import com.google.testing.littlemock.LittleMock.ArgumentMatcher;
+import com.google.testing.littlemock.LittleMock.InOrder;
+
 import junit.framework.TestCase;
 
 import java.io.IOException;
@@ -1413,16 +1418,151 @@
     } catch (IllegalStateException expected) {}
   }
 
+  public void testCustomMatcher() {
+    ArgumentMatcher argumentMatcher = new ArgumentMatcher() {
+      @Override
+      public boolean matches(Object value) {
+        return ((String) value).contains("[]");
+      }
+    };
+    mFoo.add("as[]df");
+    mFoo.add("qwer[]asdf");
+    mFoo.add("1234");
+    verify(mFoo, times(3)).add(anyString());
+    verify(mFoo, times(2)).add((String) matches(argumentMatcher));
+  }
+
+  public void testInorderExample_Success() {
+    @SuppressWarnings("unchecked")
+    List<String> firstMock = mock(List.class);
+    @SuppressWarnings("unchecked")
+    List<String> secondMock = mock(List.class);
+    firstMock.add("was called first");
+    secondMock.add("was called second");
+    InOrder inOrder = inOrder(firstMock, secondMock);
+    inOrder.verify(firstMock).add("was called first");
+    inOrder.verify(secondMock).add("was called second");
+  }
+
+  public void testInorderExample_Failure() {
+    @SuppressWarnings("unchecked")
+    List<String> firstMock = mock(List.class);
+    @SuppressWarnings("unchecked")
+    List<String> secondMock = mock(List.class);
+    firstMock.add("was called first");
+    secondMock.add("was called second");
+    InOrder inOrder = inOrder(firstMock, secondMock);
+    inOrder.verify(secondMock).add("was called second");
+    try {
+      inOrder.verify(firstMock).add("was called first");
+      throw new IllegalStateException();
+    } catch (AssertionError expected) {}
+  }
+
+  public void testInorderInterleave() {
+    @SuppressWarnings("unchecked")
+    List<String> firstMock = mock(List.class);
+    firstMock.add("a");
+    firstMock.add("b");
+    firstMock.add("a");
+
+    // Should be fine to verify a then b, since they happened in that order.
+    InOrder inOrder = inOrder(firstMock);
+    inOrder.verify(firstMock).add("a");
+    inOrder.verify(firstMock).add("b");
+
+    // Should also be fine to inorder verify the other way around, they happened in that order too.
+    inOrder = inOrder(firstMock);
+    inOrder.verify(firstMock).add("b");
+    inOrder.verify(firstMock).add("a");
+
+    // Should be fine to verify "a, b, a" since that too happened.
+    inOrder = inOrder(firstMock);
+    inOrder.verify(firstMock).add("a");
+    inOrder.verify(firstMock).add("b");
+    inOrder.verify(firstMock).add("a");
+
+    // "a, a, b" did not happen.
+    inOrder = inOrder(firstMock);
+    inOrder.verify(firstMock).add("a");
+    inOrder.verify(firstMock).add("a");
+    try {
+      inOrder.verify(firstMock).add("b");
+      throw new IllegalStateException();
+    } catch (AssertionError expected) {}
+
+    // "b, a, b" did not happen.
+    inOrder = inOrder(firstMock);
+    inOrder.verify(firstMock).add("b");
+    inOrder.verify(firstMock).add("a");
+    try {
+      inOrder.verify(firstMock).add("b");
+      throw new IllegalStateException();
+    } catch (AssertionError expected) {}
+
+    // "b" did not happen twice.
+    inOrder = inOrder(firstMock);
+    inOrder.verify(firstMock).add("b");
+    try {
+      inOrder.verify(firstMock).add("b");
+      throw new IllegalStateException();
+    } catch (AssertionError expected) {}
+  }
+
+  public void testInorderComplicatedExample() {
+    // TODO: I'm currently totally ignoring the parameters passed to the inorder method.
+    // I don't understand what the point of them is, anyway.
+    @SuppressWarnings("unchecked")
+    List<String> firstMock = mock(List.class);
+    @SuppressWarnings("unchecked")
+    List<String> secondMock = mock(List.class);
+
+    firstMock.add("1");
+    secondMock.add("2");
+    firstMock.add("3");
+    secondMock.add("4");
+
+    InOrder allInOrder = inOrder(firstMock, secondMock);
+    allInOrder.verify(firstMock).add("1");
+    allInOrder.verify(secondMock).add("2");
+    allInOrder.verify(firstMock).add("3");
+    allInOrder.verify(secondMock).add("4");
+
+    InOrder firstInOrder = inOrder(firstMock, secondMock);
+    firstInOrder.verify(firstMock).add("1");
+    firstInOrder.verify(firstMock).add("3");
+    try {
+      firstInOrder.verify(secondMock).add("2");
+      throw new IllegalStateException();
+    } catch (AssertionError expected) {}
+    firstInOrder.verify(secondMock).add("4");
+
+    InOrder secondInOrder = inOrder(firstMock, secondMock);
+    secondInOrder.verify(secondMock).add("2");
+    secondInOrder.verify(secondMock).add("4");
+    try {
+      secondInOrder.verify(firstMock).add("1");
+      throw new IllegalStateException();
+    } catch (AssertionError expected) {}
+    try {
+      secondInOrder.verify(firstMock).add("3");
+      throw new IllegalStateException();
+    } catch (AssertionError expected) {}
+  }
+
   public static class Jim {
-    public void bob() {
+    public int bob() {
       fail();
+      return 3;
     }
   }
 
   // Does not work on JVM, android only.
-  public void suppress_testMockingConcreteClasses() throws Exception {
+  public void testMockingConcreteClasses() throws Exception {
     Jim mock = mock(Jim.class);
-    mock.bob();
+    assertEquals(0, mock.bob());
+    doReturn(8).when(mock).bob();
+    assertEquals(8, mock.bob());
   }
 
   private Future<Void> invokeBarMethodAfterLatchAwait(final CountDownLatch countDownLatch) {
@@ -1439,17 +1579,6 @@
   // 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
-  // looks like this:
-  // InOrder inOrder = inOrder(firstMock, secondMock);
-  // inOrder.verify(firstMock).firstMethod();
-  // inOrder.verify(secondMock).secondMethod();
-  // This allows us to verify that the calls happened in the desired order.
-  // By far the simplest way to do this is have a static AtomicInteger on the class which
-  // indicates exactly when every method call happened, and then just compare order based on
-  // that.
-
   // TODO(hugohudson): 5. Make the doReturn() method take variable arguments.
   // The syntax is:
   // doReturn(1, 2, 3).when(mFoo).anInt();