| /* |
| * 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.facebook.buck.junit; |
| |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.hamcrest.Matchers.containsString; |
| import static org.junit.Assert.assertEquals; |
| |
| import com.facebook.buck.testutil.integration.ProjectWorkspace; |
| import com.facebook.buck.testutil.integration.ProjectWorkspace.ProcessResult; |
| import com.facebook.buck.testutil.integration.TestDataHelper; |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Function; |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.Iterables; |
| import com.google.common.io.Files; |
| |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TemporaryFolder; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.List; |
| |
| /** |
| * Integration test to verify that timeouts are handled as expected by {@link JUnitRunner}. |
| * <p> |
| * Note that there is a quirk when running tests with threads and timeouts described at |
| * https://github.com/junit-team/junit/issues/686. This test verifies that Buck honors this behavior |
| * of JUnit with its custom {@link BuckBlockJUnit4ClassRunner}. |
| * <p> |
| * That said, this behavior of JUnit interacts badly with a default test timeout in |
| * {@code .buckconfig} because it requires adding {@link org.junit.rules.Timeout} to the handful of |
| * tests that exploit this behavior. |
| */ |
| public class TimeoutIntegrationTest { |
| |
| private static final String PATH_TO_TIMEOUT_BEHAVIOR_TEST = "TimeoutChangesBehaviorTest.java"; |
| |
| @Rule |
| public TemporaryFolder temporaryFolder = new TemporaryFolder(); |
| |
| @Test |
| public void testThatTimeoutsInTestsWorkAsExpected() throws IOException { |
| ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( |
| this, "timeouts", temporaryFolder); |
| workspace.setUp(); |
| |
| // ExceedsAnnotationTimeoutTest should fail. |
| ProcessResult exceedsAnnotationTimeoutTestResult = workspace.runBuckCommand( |
| "test", "//:ExceedsAnnotationTimeoutTest"); |
| assertEquals("Test should fail due to timeout", |
| 1, |
| exceedsAnnotationTimeoutTestResult.getExitCode()); |
| assertThat(exceedsAnnotationTimeoutTestResult.getStderr(), |
| containsString( |
| "FAILURE testShouldFailDueToExpiredTimeout: test timed out after 1000 milliseconds")); |
| |
| // TimeoutChangesBehaviorTest should pass. |
| ProcessResult timeoutTestWithoutTimeout = workspace.runBuckCommand( |
| "test", "//:TimeoutChangesBehaviorTest"); |
| assertEquals(0, timeoutTestWithoutTimeout.getExitCode()); |
| |
| // TimeoutChangesBehaviorTest with @Test(timeout) specified should fail. |
| // See https://github.com/junit-team/junit/issues/686 about why it fails. |
| modifyTimeoutInTestAnnotation(PATH_TO_TIMEOUT_BEHAVIOR_TEST, /* addTimeout */ true); |
| ProcessResult timeoutTestWithTimeoutOnAnnotation = workspace.runBuckCommand( |
| "test", "//:TimeoutChangesBehaviorTest"); |
| assertEquals(1, timeoutTestWithTimeoutOnAnnotation.getExitCode()); |
| assertThat(timeoutTestWithTimeoutOnAnnotation.getStderr(), |
| containsString( |
| "FAILURE testTimeoutDictatesTheSuccessOfThisTest: " + |
| "Database should have an open transaction due to setUp().")); |
| |
| // TimeoutChangesBehaviorTest with @Rule(Timeout) should pass. |
| modifyTimeoutInTestAnnotation(PATH_TO_TIMEOUT_BEHAVIOR_TEST, /* addTimeout */ false); |
| insertTimeoutRule(PATH_TO_TIMEOUT_BEHAVIOR_TEST); |
| ProcessResult timeoutTestWithTimeoutRule = workspace.runBuckCommand( |
| "test", "//:TimeoutChangesBehaviorTest"); |
| assertEquals(0, timeoutTestWithTimeoutRule.getExitCode()); |
| |
| workspace.verify(); |
| } |
| |
| /** |
| * Swaps all instances of {@code @Test} with {@code @Test(timeout = 10000)} in the specified Java |
| * file, as determined by the value of {@code addTimeout}. |
| */ |
| private void modifyTimeoutInTestAnnotation(String path, final boolean addTimeout) |
| throws IOException { |
| Function<String, String> transform = new Function<String, String>() { |
| @Override public String apply(String line) { |
| String original = addTimeout ? "@Test" : "@Test(timeout = 100000)"; |
| String replacement = addTimeout ? "@Test(timeout = 100000)" : "@Test"; |
| return line.replace(original, replacement) + '\n'; |
| } |
| }; |
| rewriteFileWithTransform(path, transform); |
| } |
| |
| /** |
| * Inserts the following after the top-level class declaration: |
| * <pre> |
| * @org.junit.Rule |
| * public org.junit.rules.Timeout timeoutForTests = new org.junit.rules.Timeout(10000); |
| * </pre> |
| */ |
| private void insertTimeoutRule(String path) throws IOException { |
| Function<String, String> transform = new Function<String, String>() { |
| @Override |
| public String apply(String line) { |
| if (line.startsWith("public class")) { |
| return line + "\n\n" + |
| " @org.junit.Rule\n" + |
| " public org.junit.rules.Timeout timeoutForTests = " + |
| "new org.junit.rules.Timeout(10000);\n"; |
| } else { |
| return line + '\n'; |
| } |
| } |
| }; |
| rewriteFileWithTransform(path, transform); |
| } |
| |
| /** |
| * Finds the file at the specified path, transforms all of its lines using the specified |
| * {@code transform} parameter, and writes the transformed lines back to the path. |
| */ |
| private void rewriteFileWithTransform(String path, Function<String, String> transform) |
| throws IOException { |
| File javaFile = new File(temporaryFolder.getRoot(), path); |
| List<String> lines = Files.readLines(javaFile, Charsets.UTF_8); |
| String java = Joiner.on("").join(Iterables.transform(lines, transform)); |
| Files.write(java, javaFile, Charsets.UTF_8); |
| } |
| } |