/* * 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.parser; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.testutil.FakeProjectFilesystem; 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.HumanReadableException; import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.nio.file.Paths; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class ParserIntegrationTest { @Rule public TemporaryPaths temporaryFolder = new TemporaryPaths(); @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void testParserFilesAreSandboxed() throws Exception { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "parser_with_method_overrides", temporaryFolder); workspace.setUp(); BuildTarget target = workspace.newBuildTarget("//:base_genrule"); ProjectFilesystem filesystem = new FakeProjectFilesystem(); ProjectWorkspace.ProcessResult buildResult = workspace.runBuckCommand("build", "", "-v", "2"); buildResult.assertSuccess(); workspace.verify( Paths.get("base_genrule_output.expected"), BuildTargets.getGenPath(filesystem, target, "%s")); } /** * If a rule contains an erroneous dep to a non-existent rule, then it should throw an appropriate * message to help the user find the source of his error. */ @Test public void testParseRuleWithBadDependency() throws IOException { // Ensure an exception with a specific message is thrown. thrown.expect(HumanReadableException.class); thrown.expectMessage("Couldn't get dependency '//:bad-dep' of target '//:base'"); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "parse_rule_with_bad_dependency", temporaryFolder); workspace.setUp(); workspace.runBuckCommand("build", "//:base"); } /** * Creates the following graph (assume all / and \ indicate downward pointing arrows): * * <pre> * A * / \ * B C <----| * / \ / | * D E | * \ / | * F -------------- * </pre> * * Note that there is a circular dependency from C -> E -> F -> C that should be caught by the * parser. */ @Test public void testCircularDependencyDetection() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "circular_dependency_detection", temporaryFolder); workspace.setUp(); try { workspace.runBuckCommand("build", "//:A"); } catch (HumanReadableException e) { assertThat( e.getHumanReadableErrorMessage(), is( in( ImmutableSet.of( "Cycle found: //:C -> //:E -> //:F -> //:C", "Cycle found: //:E -> //:F -> //:C -> //:E", "Cycle found: //:F -> //:C -> //:E -> //:F")))); return; } fail("An exception should have been thrown because of a circular dependency."); } /** * If a .buckconfig is overridden to set allow_empty_glob to False, a glob call returning no * results will cause the build to fail. */ @Test public void testNotAllowEmptyGlob() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "not_allow_empty_glob", temporaryFolder); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("build", "//:root_module"); result.assertFailure("buck build should fail on empty glob results when set in config"); assertThat( "error message for failure to return results from glob is incorrect", result.getStderr(), containsString( "returned no results. (allow_empty_globs is set to false in the Buck " + "configuration)")); } /** By default a glob call returning no results will not cause the build to fail. */ @Test public void testAllowEmptyGlob() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "allow_empty_glob", temporaryFolder); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("build", "//:root_module"); result.assertSuccess("buck build should ignore empty glob results by default"); } @Test public void ignoredFilesAreNotReturnedByGlob() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "glob_ignores", temporaryFolder); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("build", "//:root_module"); result.assertFailure("glob should be empty because of ignores"); assertThat( "error message for failure to return results from glob is incorrect", result.getStderr(), containsString( "returned no results. (allow_empty_globs is set to false in the Buck " + "configuration)")); } @Test public void testBuildFileName() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "build_file_name", temporaryFolder); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("targets", "//:root"); result.assertSuccess("buck should parse build files with a different name"); assertEquals("//:root\n", result.getStdout()); } @Test public void testMissingName() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "missing_name", temporaryFolder); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("targets", "//..."); result.assertFailure("missing attribute should error"); assertThat(result.getStderr(), containsString("genrule")); assertThat(result.getStderr(), containsString("name")); } @Test public void testMissingRequiredAttribute() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "missing_attr", temporaryFolder); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("targets", "//:gr"); result.assertFailure("missing name should error"); assertThat(result.getStderr(), containsString("genrule")); assertThat(result.getStderr(), containsString("gr")); assertThat(result.getStderr(), containsString("out")); } @Test public void testExtraUnknownAttribute() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "extra_attr", temporaryFolder); workspace.setUp(); ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("targets", "//:gr"); result.assertFailure("extra attr should error"); assertThat(result.getStderr(), containsString("genrule")); assertThat(result.getStderr(), containsString("gr")); assertThat(result.getStderr(), containsString("blurgle")); } @Test public void testBoundaryChecksAreEnforced() throws IOException { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "package_boundaries", temporaryFolder); workspace.setUp(); try { workspace.runBuckCommand("build", "//java:foo"); fail("Expected exception"); } catch (HumanReadableException e) { assertThat(e.getMessage(), containsString("package boundary")); } workspace.addBuckConfigLocalOption("project", "check_package_boundary", "false"); workspace.runBuckCommand("build", "//java:foo").assertSuccess(); workspace.addBuckConfigLocalOption("project", "check_package_boundary", "true"); workspace.addBuckConfigLocalOption("project", "package_boundary_exceptions", "java"); workspace.runBuckCommand("build", "//java:foo").assertSuccess(); try { workspace.runBuckCommand("build", "//java2:foo").assertSuccess(); fail("Expected exception"); } catch (HumanReadableException e) { assertThat(e.getMessage(), containsString("package boundary")); } } }