blob: 3450c20f742bd9cc33819e80f966bc147aeb27d2 [file] [log] [blame]
/*
* 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.rules;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.model.BuildTargetPattern;
import com.facebook.buck.parser.BuildTargetParser;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.junit.Before;
import org.junit.Test;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Set;
@SuppressWarnings("unused") // Many unused fields in sample DTO objects.
public class ConstructorArgMarshallerTest {
private Path basePath;
private ConstructorArgMarshaller marshaller;
private BuildRuleResolver ruleResolver;
private ProjectFilesystem filesystem;
private BuildRuleType ruleType;
@Before
public void setUpInspector() {
basePath = Paths.get("example", "path");
marshaller = new ConstructorArgMarshaller();
ruleResolver = new BuildRuleResolver();
filesystem = new FakeProjectFilesystem();
ruleType = ImmutableBuildRuleType.of("example");
}
@Test
public void shouldNotPopulateAnEmptyArg() throws NoSuchBuildTargetException {
class Dto {
}
Dto dto = new Dto();
try {
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of());
} catch (RuntimeException | ConstructorArgMarshalException e) {
fail("Did not expect an exception to be thrown:\n" + Throwables.getStackTraceAsString(e));
}
}
@Test
public void shouldPopulateAStringValue()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public String name;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("name", "cheese"));
assertEquals("cheese", dto.name);
}
@Test
public void shouldPopulateABooleanValue()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public boolean value;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("value", true));
assertTrue(dto.value);
}
@Test
public void shouldPopulateBuildTargetValues()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public BuildTarget target;
public BuildTarget local;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of(
"target", "//cake:walk",
"local", ":fish"
));
assertEquals(BuildTargetFactory.newInstance("//cake:walk"), dto.target);
assertEquals(BuildTargetFactory.newInstance("//example/path:fish"), dto.local);
}
@Test
public void shouldPopulateANumericValue()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public long number;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("number", 42L));
assertEquals(42, dto.number);
}
@Test
public void shouldPopulateAPathValue()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
@Hint(name = "some_path")
public Path somePath;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("somePath", "Fish.java"));
assertEquals(Paths.get("example/path", "Fish.java"), dto.somePath);
}
@Test
public void shouldPopulateSourcePaths()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public SourcePath filePath;
public SourcePath targetPath;
}
ProjectFilesystem projectFilesystem = new FakeProjectFilesystem();
BuildTarget target = BuildTargetFactory.newInstance("//example/path:peas");
FakeBuildRule rule = new FakeBuildRule(
ruleType,
target,
new SourcePathResolver(new BuildRuleResolver()));
ruleResolver.addToIndex(rule);
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of(
"filePath", "cheese.txt",
"targetPath", ":peas"
));
assertEquals(
new PathSourcePath(projectFilesystem, Paths.get("example/path/cheese.txt")),
dto.filePath);
assertEquals(
new BuildTargetSourcePath(projectFilesystem, rule.getBuildTarget()),
dto.targetPath);
}
@Test
public void shouldPopulateAnImmutableSortedSet()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public ImmutableSortedSet<BuildTarget> deps;
}
BuildTarget t1 = BuildTargetFactory.newInstance("//please/go:here");
BuildTarget t2 = BuildTargetFactory.newInstance("//example/path:there");
Dto dto = new Dto();
// Note: the ordering is reversed from the natural ordering
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("deps", ImmutableList.of("//please/go:here", ":there")));
assertEquals(ImmutableSortedSet.of(t2, t1), dto.deps);
}
@Test
public void shouldPopulateSets()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public Set<Path> paths;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("paths", ImmutableList.of("one", "two")));
assertEquals(
ImmutableSet.of(Paths.get("example/path/one"), Paths.get("example/path/two")),
dto.paths);
}
@Test
public void shouldPopulateLists()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public List<String> list;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("list", ImmutableList.of("alpha", "beta")));
assertEquals(ImmutableList.of("alpha", "beta"), dto.list);
}
@Test
public void collectionsCanBeOptionalAndWillBeSetToAnOptionalEmptyCollectionIfMissing()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public Optional<Set<BuildTarget>> targets;
}
Dto dto = new Dto();
Map<String, Object> args = Maps.newHashMap();
args.put("targets", Lists.newArrayList());
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
args);
assertEquals(Optional.of((Set<BuildTarget>) Sets.<BuildTarget>newHashSet()), dto.targets);
}
@Test
public void optionalCollectionsWithoutAValueWillBeSetToAnEmptyOptionalCollection()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public Optional<Set<String>> strings;
}
Dto dto = new Dto();
Map<String, Object> args = Maps.newHashMap();
// Deliberately not populating args
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
args);
assertEquals(Optional.of((Set<String>) Sets.<String>newHashSet()), dto.strings);
}
@Test(expected = ConstructorArgMarshalException.class)
public void shouldBeAnErrorToAttemptToSetASingleValueToACollection()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public String file;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("file", ImmutableList.of("a", "b")));
}
@Test(expected = ConstructorArgMarshalException.class)
public void shouldBeAnErrorToAttemptToSetACollectionToASingleValue()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public Set<String> strings;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("strings", "isn't going to happen"));
}
@Test(expected = ConstructorArgMarshalException.class)
public void shouldBeAnErrorToSetTheWrongTypeOfValueInACollection()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public Set<String> strings;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("strings", ImmutableSet.of(true, false)));
}
@Test
public void shouldNormalizePaths()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public Path path;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("path", "./bar/././fish.txt"));
assertEquals(basePath.resolve("bar/fish.txt").normalize(), dto.path);
}
@Test(expected = RuntimeException.class)
public void lowerBoundGenericTypesCauseAnException()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public List<? super BuildTarget> nope;
}
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
new Dto(),
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("nope", ImmutableList.of("//will/not:happen")));
}
public void shouldSetBuildTargetParameters()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public BuildTarget single;
public BuildTarget sameBuildFileTarget;
public List<BuildTarget> targets;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of(
"single", "//com/example:cheese",
"sameBuildFileTarget", ":cake",
"targets", ImmutableList.of(":cake", "//com/example:cheese")
));
BuildTarget cheese = BuildTargetFactory.newInstance("//com/example:cheese");
BuildTarget cake = BuildTargetFactory.newInstance("//example/path:cake");
assertEquals(cheese, dto.single);
assertEquals(cake, dto.sameBuildFileTarget);
assertEquals(ImmutableList.of(cake, cheese), dto.targets);
}
@Test
public void upperBoundGenericTypesCauseValuesToBeSetToTheUpperBound()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public List<? extends SourcePath> yup;
}
ProjectFilesystem projectFilesystem = new FakeProjectFilesystem();
SourcePathResolver pathResolver = new SourcePathResolver(new BuildRuleResolver());
BuildRule rule = new FakeBuildRule(
ImmutableBuildRuleType.of("example"),
BuildTargetFactory.newInstance("//will:happen"), pathResolver);
ruleResolver.addToIndex(rule);
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of(
"yup",
ImmutableList.of(rule.getBuildTarget().getFullyQualifiedName())));
BuildTargetSourcePath path = new BuildTargetSourcePath(
projectFilesystem,
rule.getBuildTarget());
assertEquals(ImmutableList.of(path), dto.yup);
}
@Test
public void specifyingZeroIsNotConsideredOptional()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public Optional<Integer> number;
}
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of("number", 0));
assertTrue(dto.number.isPresent());
assertEquals(Optional.of(0), dto.number);
}
@Test
public void canPopulateSimpleConstructorArgFromBuildFactoryParams()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public String required;
public Optional<String> notRequired;
public int num;
// Turns out there no optional number params
//public Optional<Long> optionalLong;
public boolean needed;
public Optional<Boolean> notNeeded;
public SourcePath aSrcPath;
public Optional<SourcePath> notASrcPath;
public Path aPath;
public Optional<Path> notAPath;
}
ProjectFilesystem projectFilesystem = new FakeProjectFilesystem();
FakeBuildRule expectedRule = new FakeBuildRule(
ruleType,
BuildTargetFactory.newInstance("//example/path:path"),
new SourcePathResolver(new BuildRuleResolver()));
ruleResolver.addToIndex(expectedRule);
ImmutableMap<String, Object> args = ImmutableMap.<String, Object>builder()
.put("required", "cheese")
.put("notRequired", "cake")
// Long because that's what comes from python.
.put("num", 42L)
// Skipping optional Long.
.put("needed", true)
// Skipping optional boolean.
.put("aSrcPath", ":path")
.put("aPath", "./File.java")
.put("notAPath", "./NotFile.java")
.build();
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
args);
assertEquals("cheese", dto.required);
assertEquals("cake", dto.notRequired.get());
assertEquals(42, dto.num);
assertTrue(dto.needed);
assertEquals(Optional.<Boolean>absent(), dto.notNeeded);
BuildTargetSourcePath expected = new BuildTargetSourcePath(
projectFilesystem,
expectedRule.getBuildTarget());
assertEquals(expected, dto.aSrcPath);
assertEquals(Paths.get("example/path/NotFile.java"), dto.notAPath.get());
}
@Test
/**
* Since we populated the params from the python script, and the python script inserts default
* values instead of nulls it's never possible for someone to see "Optional.absent()", but that's
* what we want as authors of buildables. Handle that case.
*/
public void shouldPopulateDefaultValuesAsBeingAbsent()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public Optional<String> noString;
public Optional<String> defaultString;
public Optional<SourcePath> noSourcePath;
public Optional<SourcePath> defaultSourcePath;
public int primitiveNum;
public Integer wrapperObjectNum;
public boolean primitiveBoolean;
public Boolean wrapperObjectBoolean;
}
// This is not an ImmutableMap so we can test null values.
Map<String, Object> args = Maps.newHashMap();
args.put("defaultString", null);
args.put("defaultSourcePath", null);
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
args);
assertEquals(Optional.<String>absent(), dto.noString);
assertEquals(Optional.<String>absent(), dto.defaultString);
assertEquals(Optional.<SourcePath>absent(), dto.noSourcePath);
assertEquals(Optional.<SourcePath>absent(), dto.defaultSourcePath);
assertEquals(0, dto.primitiveNum);
assertEquals(Integer.valueOf(0), dto.wrapperObjectNum);
}
@Test
public void shouldResolveCollectionOfSourcePaths()
throws ConstructorArgMarshalException, NoSuchBuildTargetException {
class Dto {
public ImmutableSortedSet<SourcePath> srcs;
}
BuildRuleResolver resolver = new BuildRuleResolver();
BuildTarget target = BuildTargetFactory.newInstance("//example/path:manifest");
BuildRule rule = new FakeBuildRule(
ImmutableBuildRuleType.of("py"),
target,
new SourcePathResolver(resolver));
resolver.addToIndex(rule);
Dto dto = new Dto();
marshaller.populate(
filesystem,
buildRuleFactoryParams(),
dto,
ImmutableSet.<BuildTarget>builder(),
ImmutableSet.<BuildTargetPattern>builder(),
ImmutableMap.<String, Object>of(
"srcs",
ImmutableList.of("main.py", "lib/__init__.py", "lib/manifest.py")));
ImmutableSet<String> observedValues = FluentIterable.from(dto.srcs)
.transform(Functions.toStringFunction())
.toSet();
assertEquals(
ImmutableSet.of(
"example/path/main.py",
"example/path/lib/__init__.py",
"example/path/lib/manifest.py"),
observedValues);
}
public BuildRuleFactoryParams buildRuleFactoryParams() {
BuildTargetParser parser = new BuildTargetParser();
BuildTarget target = BuildTargetFactory.newInstance("//example/path:three");
return NonCheckingBuildRuleFactoryParams.createNonCheckingBuildRuleFactoryParams(
parser,
target);
}
}