/*
* 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.shell;
import static com.facebook.buck.testutil.MoreAsserts.assertIterablesEquals;
import static java.util.Collections.singleton;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultBuildTargetSourcePath;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.FakeBuildContext;
import com.facebook.buck.rules.FakeBuildRule;
import com.facebook.buck.rules.FakeBuildableContext;
import com.facebook.buck.rules.FakeSourcePath;
import com.facebook.buck.rules.PathSourcePath;
import com.facebook.buck.rules.RuleKey;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.keys.DefaultRuleKeyFactory;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.TestExecutionContext;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.facebook.buck.testutil.MoreAsserts;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.cache.DefaultFileHashCache;
import com.facebook.buck.util.cache.FileHashCache;
import com.facebook.buck.util.cache.StackedFileHashCache;
import com.google.common.collect.ImmutableList;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class ExportFileTest {
private ProjectFilesystem projectFilesystem;
private BuildTarget target;
@Rule public ExpectedException expectedException = ExpectedException.none();
@Before
public void createFixtures() throws InterruptedException {
projectFilesystem = FakeProjectFilesystem.createJavaOnlyFilesystem();
target = BuildTargetFactory.newInstance(projectFilesystem.getRootPath(), "//:example.html");
}
@Test
public void shouldSetSrcAndOutToNameParameterIfNeitherAreSet() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
ExportFile exportFile =
ExportFileBuilder.newExportFileBuilder(target).build(resolver, projectFilesystem);
List<Step> steps =
exportFile.getBuildSteps(
FakeBuildContext.withSourcePathResolver(pathResolver), new FakeBuildableContext());
MoreAsserts.assertSteps(
"The output directory should be created and then the file should be copied there.",
ImmutableList.of(
"mkdir -p " + projectFilesystem.resolve("buck-out/gen"),
"rm -f -r " + projectFilesystem.resolve("buck-out/gen/example.html"),
"cp "
+ projectFilesystem.resolve("example.html")
+ " "
+ Paths.get("buck-out/gen/example.html")),
steps,
TestExecutionContext.newInstance());
assertEquals(
projectFilesystem.getBuckPaths().getGenDir().resolve("example.html"),
pathResolver.getRelativePath(exportFile.getSourcePathToOutput()));
}
@Test
public void shouldSetOutToNameParamValueIfSrcIsSet() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
ExportFile exportFile =
ExportFileBuilder.newExportFileBuilder(target)
.setOut("fish")
.build(resolver, projectFilesystem);
List<Step> steps =
exportFile.getBuildSteps(
FakeBuildContext.withSourcePathResolver(pathResolver), new FakeBuildableContext());
MoreAsserts.assertSteps(
"The output directory should be created and then the file should be copied there.",
ImmutableList.of(
"mkdir -p " + projectFilesystem.resolve("buck-out/gen"),
"rm -f -r " + projectFilesystem.resolve("buck-out/gen/fish"),
"cp "
+ projectFilesystem.resolve("example.html")
+ " "
+ Paths.get("buck-out/gen/fish")),
steps,
TestExecutionContext.newInstance());
assertEquals(
projectFilesystem.getBuckPaths().getGenDir().resolve("fish"),
pathResolver.getRelativePath(exportFile.getSourcePathToOutput()));
}
@Test
public void shouldSetOutAndSrcAndNameParametersSeparately() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
ExportFile exportFile =
ExportFileBuilder.newExportFileBuilder(target)
.setSrc(new PathSourcePath(projectFilesystem, Paths.get("chips")))
.setOut("fish")
.build(resolver, projectFilesystem);
List<Step> steps =
exportFile.getBuildSteps(
FakeBuildContext.withSourcePathResolver(pathResolver), new FakeBuildableContext());
MoreAsserts.assertSteps(
"The output directory should be created and then the file should be copied there.",
ImmutableList.of(
"mkdir -p " + projectFilesystem.resolve("buck-out/gen"),
"rm -f -r " + projectFilesystem.resolve("buck-out/gen/fish"),
"cp " + projectFilesystem.resolve("chips") + " " + Paths.get("buck-out/gen/fish")),
steps,
TestExecutionContext.newInstance());
assertEquals(
projectFilesystem.getBuckPaths().getGenDir().resolve("fish"),
pathResolver.getRelativePath(exportFile.getSourcePathToOutput()));
}
@Test
public void shouldSetInputsFromSourcePaths() throws Exception {
ExportFileBuilder builder =
ExportFileBuilder.newExportFileBuilder(target)
.setSrc(new FakeSourcePath("chips"))
.setOut("cake");
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
ExportFile exportFile = builder.build(resolver, projectFilesystem);
assertIterablesEquals(
singleton(Paths.get("chips")),
pathResolver.filterInputsToCompareToOutput(exportFile.getSource()));
resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
FakeBuildRule rule =
resolver.addToIndex(
new FakeBuildRule(BuildTargetFactory.newInstance("//example:one"), pathResolver));
builder.setSrc(new DefaultBuildTargetSourcePath(rule.getBuildTarget()));
exportFile = builder.build(resolver, projectFilesystem);
assertThat(
pathResolver.filterInputsToCompareToOutput(exportFile.getSource()), Matchers.empty());
resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
builder.setSrc(null);
exportFile = builder.build(resolver, projectFilesystem);
assertIterablesEquals(
singleton(projectFilesystem.getPath("example.html")),
pathResolver.filterInputsToCompareToOutput(exportFile.getSource()));
}
@Test
public void getOutputName() throws Exception {
ExportFile exportFile =
ExportFileBuilder.newExportFileBuilder(target)
.setOut("cake")
.build(
new BuildRuleResolver(
TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()),
projectFilesystem);
assertEquals("cake", exportFile.getOutputName());
}
@Test
public void modifyingTheContentsOfTheFileChangesTheRuleKey() throws Exception {
Path root = Files.createTempDirectory("root");
FakeProjectFilesystem filesystem = new FakeProjectFilesystem(root);
Path temp = Paths.get("example_file");
FileHashCache hashCache =
new StackedFileHashCache(
ImmutableList.of(DefaultFileHashCache.createDefaultFileHashCache(filesystem)));
SourcePathRuleFinder ruleFinder =
new SourcePathRuleFinder(
new BuildRuleResolver(
TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()));
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
DefaultRuleKeyFactory ruleKeyFactory =
new DefaultRuleKeyFactory(0, hashCache, resolver, ruleFinder);
filesystem.writeContentsToPath("I like cheese", temp);
ExportFileBuilder builder =
ExportFileBuilder.newExportFileBuilder(BuildTargetFactory.newInstance("//some:file"))
.setSrc(new PathSourcePath(filesystem, temp));
ExportFile rule =
builder.build(
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()),
filesystem);
RuleKey original = ruleKeyFactory.build(rule);
filesystem.writeContentsToPath("I really like cheese", temp);
// Create a new rule. The FileHashCache held by the existing rule will retain a reference to the
// previous content of the file, so we need to create an identical rule.
rule =
builder.build(
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()),
filesystem);
hashCache =
new StackedFileHashCache(
ImmutableList.of(DefaultFileHashCache.createDefaultFileHashCache(filesystem)));
ruleFinder =
new SourcePathRuleFinder(
new BuildRuleResolver(
TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()));
resolver = new SourcePathResolver(ruleFinder);
ruleKeyFactory = new DefaultRuleKeyFactory(0, hashCache, resolver, ruleFinder);
RuleKey refreshed = ruleKeyFactory.build(rule);
assertNotEquals(original, refreshed);
}
@Test
public void referenceModeUsesUnderlyingSourcePath() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
SourcePath src = new FakeSourcePath(projectFilesystem, "source");
ExportFile exportFile =
ExportFileBuilder.newExportFileBuilder(target)
.setMode(ExportFileDescription.Mode.REFERENCE)
.setSrc(src)
.build(resolver, projectFilesystem);
assertThat(
pathResolver.getRelativePath(exportFile.getSourcePathToOutput()),
Matchers.equalTo(pathResolver.getRelativePath(src)));
}
@Test
public void referenceModeRequiresSameFilesystem() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ProjectFilesystem differentFilesystem = new FakeProjectFilesystem();
SourcePath src = new FakeSourcePath(differentFilesystem, "source");
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(Matchers.containsString("must use `COPY` mode"));
ExportFileBuilder.newExportFileBuilder(target)
.setMode(ExportFileDescription.Mode.REFERENCE)
.setSrc(src)
.build(resolver, projectFilesystem);
}
@Test
public void referenceModeDoesNotAcceptOutParameter() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(Matchers.containsString("must not set `out`"));
ExportFileBuilder.newExportFileBuilder(target)
.setOut("out")
.setMode(ExportFileDescription.Mode.REFERENCE)
.build(resolver, projectFilesystem);
}
}