| /* |
| * 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.model.BuildTarget; |
| import com.facebook.buck.model.BuildTargetFactory; |
| import com.facebook.buck.parser.BuildTargetParser; |
| import com.facebook.buck.util.ProjectFilesystem; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Throwables; |
| 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.io.File; |
| 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 ArgObjectPopulatomaticTest { |
| |
| private Path basePath; |
| private ArgObjectPopulatomatic inspector; |
| private BuildRuleResolver ruleResolver; |
| |
| @Before |
| public void setUpInspector() { |
| basePath = Paths.get("example", "path"); |
| inspector = new ArgObjectPopulatomatic(basePath); |
| ruleResolver = new BuildRuleResolver(); |
| } |
| |
| @Test |
| public void shouldNotPopulateAnEmptyArg() { |
| class Dto { |
| } |
| |
| Dto dto = new Dto(); |
| try { |
| inspector.populate( |
| ruleResolver, buildRuleFactoryParams(ImmutableMap.<String, Object>of()), dto); |
| } catch (RuntimeException e) { |
| fail("Did not expect an exception to be thrown:\n" + Throwables.getStackTraceAsString(e)); |
| } |
| } |
| |
| @Test |
| public void shouldPopulateAStringValue() { |
| class Dto { |
| public String name; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of("name", "cheese")), |
| dto); |
| |
| assertEquals("cheese", dto.name); |
| } |
| |
| @Test |
| public void shouldPopulateABooleanValue() { |
| class Dto { |
| public boolean value; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate(ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of("value", true)), |
| dto); |
| |
| assertTrue(dto.value); |
| } |
| |
| @Test |
| public void shouldPopulateBuildTargetValues() { |
| class Dto { |
| public BuildTarget target; |
| public BuildTarget local; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate(ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "target", "//cake:walk", |
| "local", ":fish" |
| )), |
| dto); |
| |
| assertEquals(BuildTargetFactory.newInstance("//cake:walk"), dto.target); |
| assertEquals(BuildTargetFactory.newInstance("//example/path:fish"), dto.local); |
| } |
| |
| @Test |
| public void shouldPopulateANumericValue() { |
| class Dto { |
| public long number; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate(ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of("number", 42L)), |
| dto); |
| |
| assertEquals(42, dto.number); |
| } |
| |
| @Test |
| public void shouldPopulateAPathValue() { |
| class Dto { |
| @Hint(name = "some_path") |
| public Path somePath; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate(ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of("some_path", "Fish.java")), |
| dto); |
| |
| assertEquals(Paths.get("example/path", "Fish.java"), dto.somePath); |
| } |
| |
| @Test |
| public void shouldPopulateSourcePaths() { |
| class Dto { |
| public SourcePath filePath; |
| public SourcePath targetPath; |
| } |
| |
| BuildTarget target = BuildTargetFactory.newInstance("//example/path:peas"); |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "filePath", "cheese.txt", |
| "targetPath", ":peas" |
| )), |
| dto); |
| |
| assertEquals(new FileSourcePath("cheese.txt"), dto.filePath); |
| assertEquals(new BuildTargetSourcePath(target), dto.targetPath); |
| } |
| |
| @Test |
| public void shouldPopulateAnImmutableSortedSet() { |
| 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 |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "deps", ImmutableList.of("//please/go:here", ":there"))), |
| dto); |
| |
| assertEquals(ImmutableSortedSet.of(t2, t1), dto.deps); |
| } |
| |
| @Test |
| public void shouldPopulateSets() { |
| class Dto { |
| public Set<Path> paths; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "paths", ImmutableList.of("one", "two"))), |
| dto); |
| |
| assertEquals( |
| ImmutableSet.of(Paths.get("example/path/one"), Paths.get("example/path/two")), |
| dto.paths); |
| } |
| |
| @Test |
| public void shouldPopulateLists() { |
| class Dto { |
| public List<String> list; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "list", ImmutableList.of("alpha", "beta"))), |
| dto); |
| |
| assertEquals(ImmutableList.of("alpha", "beta"), dto.list); |
| } |
| |
| @Test |
| public void collectionsCanBeOptionalAndWillBeSetToAnOptionalEmptyCollectionIfMissing() { |
| class Dto { |
| public Optional<Set<BuildTarget>> targets; |
| } |
| |
| Dto dto = new Dto(); |
| Map<String, Object> args = Maps.newHashMap(); |
| args.put("targets", Lists.newArrayList()); |
| |
| inspector.populate(ruleResolver, buildRuleFactoryParams(args), dto); |
| |
| assertEquals(Optional.of(Sets.newHashSet()), dto.targets); |
| } |
| |
| @Test(expected = RuntimeException.class) |
| public void shouldBeAnErrorToAttemptToSetASingleValueToACollection() { |
| class Dto { |
| public String file; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of("file", ImmutableList.of("a", "b"))), |
| dto); |
| } |
| |
| @Test(expected = RuntimeException.class) |
| public void shouldBeAnErrorToAttemptToSetACollectionToASingleValue() { |
| class Dto { |
| public Set<String> strings; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of("strings", "isn't going to happen")), |
| dto); |
| } |
| |
| @Test(expected = RuntimeException.class) |
| public void shouldBeAnErrorToSetTheWrongTypeOfValueInACollection() { |
| class Dto { |
| public Set<String> strings; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "strings", ImmutableSet.of(true, false))), |
| dto); |
| } |
| |
| @Test |
| public void shouldNormalizePaths() { |
| class Dto { |
| public Path path; |
| } |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of("path", "./foo/../bar/./fish.txt")), |
| dto); |
| |
| assertEquals(basePath.resolve("bar/fish.txt").normalize(), dto.path); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void lowerBoundGenericTypesCauseAnException() { |
| class Dto { |
| public List<? super BuildTarget> nope; |
| } |
| |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "nope", ImmutableList.of("//will/not:happen"))), |
| new Dto()); |
| } |
| |
| public void shouldSetBuildTargetParameters() { |
| class Dto { |
| public BuildTarget single; |
| public BuildTarget sameBuildFileTarget; |
| public List<BuildTarget> targets; |
| } |
| Dto dto = new Dto(); |
| |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "single", "//com/example:cheese", |
| "sameBuildFileTarget", ":cake", |
| "targets", ImmutableList.of(":cake", "//com/example:cheese") |
| )), |
| dto); |
| |
| 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 shouldSetBuildRulesIfRequested() { |
| class Dto { |
| public BuildRule directDep; |
| } |
| |
| BuildTarget target = BuildTargetFactory.newInstance("//some/exmaple:target"); |
| BuildRule rule = new FakeBuildRule(new BuildRuleType("example"), target); |
| |
| BuildRuleResolver resolver = new BuildRuleResolver(ImmutableMap.of(target, rule)); |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| resolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "directDep", target.getFullyQualifiedName())), |
| dto); |
| |
| assertEquals(dto.directDep, rule); |
| } |
| |
| @Test |
| public void upperBoundGenericTypesCauseValuesToBeSetToTheUpperBound() { |
| class Dto { |
| public List<? extends SourcePath> yup; |
| } |
| |
| BuildTarget target = BuildTargetFactory.newInstance("//will:happen"); |
| Dto dto = new Dto(); |
| inspector.populate( |
| ruleResolver, |
| buildRuleFactoryParams(ImmutableMap.<String, Object>of( |
| "yup", ImmutableList.of(target.getFullyQualifiedName()))), |
| dto); |
| |
| BuildTargetSourcePath path = new BuildTargetSourcePath(target); |
| assertEquals(ImmutableList.of(path), dto.yup); |
| } |
| |
| @Test |
| public void canPopulateSimpleConstructorArgFromBuildFactoryParams() { |
| 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; |
| // Turns out there are no optional boolean params |
| //public Optional<Boolean> notNeeded; |
| |
| public SourcePath aSrcPath; |
| public Optional<SourcePath> notASrcPath; |
| |
| public Path aPath; |
| public Optional<Path> notAPath; |
| } |
| |
| 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(); |
| inspector.populate(ruleResolver, buildRuleFactoryParams(args), dto); |
| |
| assertEquals("cheese", dto.required); |
| assertEquals("cake", dto.notRequired.get()); |
| assertEquals(42, dto.num); |
| assertTrue(dto.needed); |
| BuildTargetSourcePath expected = new BuildTargetSourcePath( |
| BuildTargetFactory.newInstance("//example/path:path")); |
| 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() { |
| class Dto { |
| public Optional<String> noString; |
| public Optional<String> defaultString; |
| |
| public Optional<SourcePath> noSourcePath; |
| public Optional<SourcePath> defaultSourcePath; |
| } |
| |
| ImmutableMap<String, Object> args = ImmutableMap.<String, Object>builder() |
| .put("defaultString", "") |
| .put("defaultSourcePath", "") |
| .build(); |
| Dto dto = new Dto(); |
| inspector.populate(ruleResolver, buildRuleFactoryParams(args), dto); |
| |
| assertEquals(Optional.absent(), dto.noString); |
| assertEquals(Optional.absent(), dto.defaultString); |
| assertEquals(Optional.absent(), dto.noSourcePath); |
| assertEquals(Optional.absent(), dto.defaultSourcePath); |
| } |
| |
| @Test |
| public void shouldResolveBuildRulesFromTargetsAndAssignToFields() { |
| class Dto { |
| public BuildRule rule; |
| } |
| |
| BuildTarget target = BuildTargetFactory.newInstance("//i/love:lucy"); |
| BuildRule rule = new FakeBuildRule(new BuildRuleType("example"), target); |
| BuildRuleResolver resolver = new BuildRuleResolver(ImmutableMap.of(target, rule)); |
| |
| Dto dto = new Dto(); |
| inspector.populate( |
| resolver, |
| buildRuleFactoryParams( |
| ImmutableMap.<String, Object>of("rule", target.getFullyQualifiedName())), |
| dto); |
| |
| assertEquals(rule, dto.rule); |
| } |
| |
| public BuildRuleFactoryParams buildRuleFactoryParams(Map<String, Object> args) { |
| ProjectFilesystem filesystem = new ProjectFilesystem(new File(".")) { |
| @Override |
| public boolean exists(String pathRelativeToProjectRoot) { |
| return true; |
| } |
| }; |
| BuildTargetParser parser = new BuildTargetParser(filesystem); |
| BuildTarget target = BuildTargetFactory.newInstance("//example/path:three"); |
| return NonCheckingBuildRuleFactoryParams.createNonCheckingBuildRuleFactoryParams( |
| args, parser, target); |
| } |
| } |