JUnit before/after call flow test.

Summary:
Simple test that check whether the order of calls for
@BeforeClass @AfterClass @Before and @After is correct.
diff --git a/test/com/facebook/buck/junit/CallFlowIntegrationTest.java b/test/com/facebook/buck/junit/CallFlowIntegrationTest.java
new file mode 100644
index 0000000..f22a5ae
--- /dev/null
+++ b/test/com/facebook/buck/junit/CallFlowIntegrationTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013-present Facebook, 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.facebook.buck.junit;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+
+import com.facebook.buck.testutil.integration.ProjectWorkspace;
+import com.facebook.buck.testutil.integration.ProjectWorkspace.ProcessResult;
+import com.facebook.buck.testutil.integration.TestDataHelper;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.IOException;
+
+public class CallFlowIntegrationTest {
+
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  @Test
+  public void testCallFlow() throws IOException {
+    ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
+        this, "test_call_flow", temporaryFolder);
+    workspace.setUp();
+
+    // ExceedsAnnotationTimeoutTest should fail.
+    ProcessResult suiteTestResult = workspace.runBuckCommand("test", "//:example");
+    suiteTestResult.assertExitCode(
+        "Test should fail because @AfterClass method is supposed to call exit(42)",
+        1);
+    assertThat(suiteTestResult.getStderr(), containsString("exit code 42"));
+  }
+
+}
diff --git a/test/com/facebook/buck/junit/testdata/test_call_flow/BUCK b/test/com/facebook/buck/junit/testdata/test_call_flow/BUCK
new file mode 100644
index 0000000..06b3008
--- /dev/null
+++ b/test/com/facebook/buck/junit/testdata/test_call_flow/BUCK
@@ -0,0 +1,26 @@
+java_test(
+  name = 'example',
+  srcs = glob(['*.java']),
+  deps = [
+    ':junit',
+  ],
+)
+
+prebuilt_jar(
+  name = 'junit',
+  binary_jar = 'junit-4.11.jar',
+  deps = [
+    ':hamcrest-core',
+    ':hamcrest-library',
+  ],
+)
+
+prebuilt_jar(
+  name = 'hamcrest-core',
+  binary_jar = 'hamcrest-core-1.3.jar',
+)
+
+prebuilt_jar(
+  name = 'hamcrest-library',
+  binary_jar = 'hamcrest-library-1.3.jar',
+)
diff --git a/test/com/facebook/buck/junit/testdata/test_call_flow/CallFlowTest.java b/test/com/facebook/buck/junit/testdata/test_call_flow/CallFlowTest.java
new file mode 100644
index 0000000..d4295a5
--- /dev/null
+++ b/test/com/facebook/buck/junit/testdata/test_call_flow/CallFlowTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012-present Facebook, 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.example;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * This test verifies that all junit before/after functions
+ * gets called in correct order:
+ *
+ * @BeforeClass - oneTimeSetUp
+ * @Before - setUp
+ * @Test - testSomething(1 or 2)
+ * @After - tearDown
+ * @Before - setUp
+ * @Test - testSomething(1 or 2)
+ * @After - tearDown
+ * @AfterClass - oneTimeTearDown
+ *
+ * JUnit don't fail a test when @AfterClass or @BeforeClass methods throw an Exception
+ * so making assertions within those methods doesn't make sense. Instead we call System.exit
+ * from @AfterClass and will verify if test fails due to invalid return code.
+ */
+public class CallFlowTest {
+
+  private static int beforeClassCallCounter;
+  private static boolean afterClassCalled;
+  private static boolean isWithinBeforeAndAfter;
+  private static int testCallCounter;
+
+  @BeforeClass
+  public static void oneTimeSetUp() {
+    beforeClassCallCounter++;
+  }
+
+  @Before
+  public void setUp() {
+    assertEquals(1, beforeClassCallCounter);
+    assertFalse(isWithinBeforeAndAfter);
+    isWithinBeforeAndAfter = true;
+  }
+
+  @After
+  public void tearDown() {
+    assertTrue(isWithinBeforeAndAfter);
+    isWithinBeforeAndAfter = false;
+  }
+
+  @AfterClass
+  public static void oneTimeTearDown() throws Exception {
+    if (!afterClassCalled && beforeClassCallCounter == 1 && testCallCounter == 2
+        && !isWithinBeforeAndAfter) {
+      System.exit(42);
+    }
+    afterClassCalled = true;
+  }
+
+  private void something() {
+    assertTrue(isWithinBeforeAndAfter);
+    testCallCounter++;
+  }
+
+  @Test
+  public void testSomething1() {
+    something();
+  }
+
+  @Test
+  public void testSomething2() {
+    something();
+  }
+
+}
diff --git a/test/com/facebook/buck/junit/testdata/test_call_flow/hamcrest-core-1.3.jar b/test/com/facebook/buck/junit/testdata/test_call_flow/hamcrest-core-1.3.jar
new file mode 100644
index 0000000..9d5fe16
--- /dev/null
+++ b/test/com/facebook/buck/junit/testdata/test_call_flow/hamcrest-core-1.3.jar
Binary files differ
diff --git a/test/com/facebook/buck/junit/testdata/test_call_flow/hamcrest-library-1.3.jar b/test/com/facebook/buck/junit/testdata/test_call_flow/hamcrest-library-1.3.jar
new file mode 100644
index 0000000..9eac80d
--- /dev/null
+++ b/test/com/facebook/buck/junit/testdata/test_call_flow/hamcrest-library-1.3.jar
Binary files differ
diff --git a/test/com/facebook/buck/junit/testdata/test_call_flow/junit-4.11.jar b/test/com/facebook/buck/junit/testdata/test_call_flow/junit-4.11.jar
new file mode 100644
index 0000000..aaf7444
--- /dev/null
+++ b/test/com/facebook/buck/junit/testdata/test_call_flow/junit-4.11.jar
Binary files differ
diff --git a/test/com/facebook/buck/testutil/integration/ProjectWorkspace.java b/test/com/facebook/buck/testutil/integration/ProjectWorkspace.java
index 307a5b5..fe464d5 100644
--- a/test/com/facebook/buck/testutil/integration/ProjectWorkspace.java
+++ b/test/com/facebook/buck/testutil/integration/ProjectWorkspace.java
@@ -156,6 +156,10 @@
     }
 
     public void assertExitCode(int exitCode) {
+      assertExitCode(null, exitCode);
+    }
+
+    public void assertExitCode(String message, int exitCode) {
       if (exitCode == getExitCode()) {
         return;
       }
@@ -166,7 +170,11 @@
       System.err.println(getStderr());
       System.err.println("=== STDOUT ===");
       System.err.println(getStdout());
-      fail(failureMessage);
+      if (message != null) {
+        fail(message);
+      } else {
+        fail(failureMessage);
+      }
     }
   }