/* * Copyright 2014-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.jvm.java; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import com.facebook.buck.testutil.integration.ProjectWorkspace; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.facebook.buck.testutil.integration.TestDataHelper; import com.facebook.buck.util.environment.Platform; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Ordering; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; public class JavaTestIntegrationTest { @Rule public TemporaryPaths temp = new TemporaryPaths(); @Test public void shouldNotCompileIfDependsOnCompilerClasspath() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "missing_test_deps", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("build", "//:no-jsr-305"); result.assertFailure(); String stderr = result.getStderr(); assertTrue(stderr, stderr.contains("cannot find symbol\nimport javax.annotation.Nullable;")); } @Test public void shouldRefuseToRunJUnitTestsIfHamcrestNotOnClasspath() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "missing_test_deps", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:no-hamcrest"); // The bug this addresses was exposed as a missing output XML files. We expect the test to fail // with a warning to the user explaining that hamcrest was missing. result.assertTestFailure(); String stderr = result.getStderr(); assertTrue( stderr, stderr.contains( "Unable to locate hamcrest on the classpath. Please add as a test dependency.")); } @Test public void shouldRefuseToRunJUnitTestsIfJUnitNotOnClasspath() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "missing_test_deps", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:no-junit"); // The bug this address was exposed as a missing output XML files. We expect the test to fail // with a warning to the user explaining that hamcrest was missing. result.assertTestFailure(); String stderr = result.getStderr(); assertTrue( stderr, stderr.contains( "Unable to locate junit on the classpath. Please add as a test dependency.")); } @Test public void shouldRefuseToRunTestNgTestsIfTestNgNotOnClasspath() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "missing_test_deps", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:no-testng"); result.assertTestFailure(); String stderr = result.getStderr(); assertTrue( stderr, stderr.contains( "Unable to locate testng on the classpath. Please add as a test dependency.")); } /** * There's a requirement that the JUnitRunner creates and runs tests on the same thread (thanks to * jmock having a thread guard), but we don't want to create lots of threads. Because of this the * runner uses one SingleThreadExecutor to run all tests. However, if one test schedules another * (as is the case with Suites and sub-tests) _and_ the buck config says that we're going to use a * custom timeout for tests, then both tests are created and executed using the same single thread * executor, in the following order: * * <p>create suite -> create test -> run suite -> run test * * <p>Obviously, that "run test" causes the deadlock, since suite hasn't finished executing and * won't until test completes, but test won't be run until suite finishes. Furrfu. */ @Test public void shouldNotDeadlock() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "deadlock", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:suite"); result.assertSuccess(); } @Test public void missingResultsFileIsTestFailure() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "java_test_missing_result_file", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:simple"); result.assertSpecialExitCode("test should fail", 42); String stderr = result.getStderr(); assertTrue(stderr, stderr.contains("test exited before generating results file")); } @Test public void spinningTestTimesOutGlobalTimeout() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "slow_tests", temp); workspace.setUp(); workspace.writeContentsToPath("[test]\n rule_timeout = 250", ".buckconfig"); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:spinning"); result.assertSpecialExitCode("test should fail", 42); String stderr = result.getStderr(); assertTrue(stderr, stderr.contains("test timed out before generating results file")); assertThat(stderr, Matchers.containsString("FAIL")); assertThat(stderr, Matchers.containsString("250ms")); } @Test public void spinningTestTimesOutPerRuleTimeout() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "slow_tests_per_rule_timeout", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:spinning"); result.assertSpecialExitCode("test should fail", 42); String stderr = result.getStderr(); assertTrue(stderr, stderr.contains("test timed out before generating results file")); assertThat(stderr, Matchers.containsString("FAIL")); assertThat(stderr, Matchers.containsString("100ms")); } @Test public void normalTestDoesNotTimeOut() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "slow_tests", temp); workspace.setUp(); workspace.writeContentsToPath("[test]\n rule_timeout = 10000", ".buckconfig"); workspace.runBuckCommand("test", "//:slow").assertSuccess(); } @Test public void brokenTestGivesFailedTestResult() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "java_test_broken_test", temp); workspace.setUp(); workspace.runBuckCommand("test", "//:simple").assertTestFailure(); } @Test public void staticInitializationException() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "static_initialization_test", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:npe"); result.assertTestFailure(); assertThat( result.getStderr(), Matchers.containsString("com.facebook.buck.example.StaticErrorTest")); } @Test public void dependencyOnAnotherTest() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "depend_on_another_test", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:a"); result.assertSuccess(); } @Test public void testWithJni() throws IOException { assumeTrue(Platform.detect() != Platform.WINDOWS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "test_with_jni", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:jtest"); result.assertSuccess(); } @Test public void testWithJniWithWhitelist() throws IOException { assumeTrue(Platform.detect() != Platform.WINDOWS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "test_with_jni", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:jtest-skip-dep"); result.assertSuccess(); } @Test public void testWithJniWithWhitelistAndDangerousSymlink() throws IOException { assumeTrue(Platform.detect() != Platform.WINDOWS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "test_with_jni", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result1 = workspace.runBuckCommand("test", "//:jtest-pernicious", "//:jtest-symlink"); result1.assertSuccess(); workspace.replaceFileContents("BUCK", "'//:jlib-native',#delete-1", ""); workspace.replaceFileContents("JTestWithoutPernicious.java", "@Test//getValue", ""); workspace.replaceFileContents("JTestWithoutPernicious.java", "//@Test//noTestLib", "@Test"); ProjectWorkspace.ProcessResult result2 = workspace.runBuckCommand("test", "//:jtest-pernicious", "//:jtest-symlink"); result2.assertSuccess(); } @Test public void testForkMode() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "slow_tests", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("test", "//:fork-mode"); result.assertSuccess(); } @Test public void testClasspath() throws IOException { final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "test_rule_classpath", temp); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("audit", "classpath", "//:top"); result.assertSuccess(); ImmutableSortedSet<Path> actualPaths = FluentIterable.from(Arrays.asList(result.getStdout().split("\\s+"))) .transform(input -> temp.getRoot().relativize(Paths.get(input))) .toSortedSet(Ordering.natural()); ImmutableSortedSet<Path> expectedPaths = ImmutableSortedSet.of( Paths.get("buck-out/gen/lib__top__output/top.jar"), Paths.get("buck-out/gen/lib__direct_dep__output/direct_dep.jar"), Paths.get("buck-out/gen/lib__mid_test#testsjar__output/mid_test#testsjar.jar"), Paths.get("buck-out/gen/lib__transitive_lib__output/transitive_lib.jar")); assertEquals(expectedPaths, actualPaths); } }