public BottomUpPathMerger(
        Iterable<IjFolder> foldersToWalk, int limit, PackagePathCache packagePathCache) {
      this.tree = new MutableDirectedGraph<>();
      this.packagePathCache = packagePathCache;
      this.mergePathsMap = new HashMap<>();

      for (IjFolder folder : foldersToWalk) {
        mergePathsMap.put(folder.getPath(), folder);

        Path path = folder.getPath();
        while (path.getNameCount() > limit) {
          Path parent = path.getParent();
          if (parent == null) {
            break;
          }

          boolean isParentAndGrandParentAlreadyInTree = tree.containsNode(parent);
          tree.addEdge(parent, path);
          if (isParentAndGrandParentAlreadyInTree) {
            break;
          }

          path = parent;
        }
      }
    }
  private DependencyGraph createDependencyGraphFromBuildRules(Iterable<? extends BuildRule> rules) {
    MutableDirectedGraph<BuildRule> graph = new MutableDirectedGraph<BuildRule>();
    for (BuildRule rule : rules) {
      for (BuildRule dep : rule.getDeps()) {
        graph.addEdge(rule, dep);
      }
    }

    return new DependencyGraph(graph);
  }
  /** Verify that owners are correctly detected: - one owner, multiple inputs, json output */
  @Test
  public void verifyInputsWithOneOwnerAreCorrectlyReportedInJson()
      throws CmdLineException, IOException {
    // All files will be directories now
    FakeProjectFilesystem filesystem =
        new FakeProjectFilesystem() {
          @Override
          public File getFileForRelativePath(String pathRelativeToProjectRoot) {
            return new ExistingFile(getProjectRoot(), pathRelativeToProjectRoot);
          }
        };

    // Create inputs
    String[] args =
        new String[] {
          "java/somefolder/badfolder/somefile.java",
          "java/somefolder/perfect.java",
          "com/test/subtest/random.java"
        };
    ImmutableSortedSet<Path> inputs = MorePaths.asPaths(ImmutableSortedSet.copyOf(args));

    // Build rule that owns all inputs
    BuildTarget target = new BuildTarget("//base/name", "name");
    BuildRule ownerRule = new StubBuildRule(target, inputs);

    // Create graph
    MutableDirectedGraph<BuildRule> mutableGraph = new MutableDirectedGraph<BuildRule>();
    mutableGraph.addNode(ownerRule);

    DependencyGraph graph = new DependencyGraph(mutableGraph);

    // Create options
    AuditOwnerOptions options = getOptions(args);

    // Create command under test
    AuditOwnerCommand command = createAuditOwnerCommand(filesystem);

    // Generate report and verify nonFileInputs are filled in as expected.
    AuditOwnerCommand.OwnersReport report = command.generateOwnersReport(graph, options);
    command.printOwnersOnlyJsonReport(report);

    String expectedJson =
        Joiner.on("")
            .join(
                "{",
                "\"com/test/subtest/random.java\":[\"//base/name:name\"],",
                "\"java/somefolder/badfolder/somefile.java\":[\"//base/name:name\"],",
                "\"java/somefolder/perfect.java\":[\"//base/name:name\"]",
                "}");

    assertEquals(expectedJson, console.getTextWrittenToStdOut());
    assertEquals("", console.getTextWrittenToStdErr());
  }
  /** Verify that owners are correctly detected: - inputs that belong to multiple targets */
  @Test
  public void verifyInputsWithMultipleOwnersAreCorrectlyReported() throws CmdLineException {
    // All files will be directories now
    FakeProjectFilesystem filesystem =
        new FakeProjectFilesystem() {
          @Override
          public File getFileForRelativePath(String pathRelativeToProjectRoot) {
            return new ExistingFile(getProjectRoot(), pathRelativeToProjectRoot);
          }
        };

    // Create inputs
    String[] args =
        new String[] {
          "java/somefolder/badfolder/somefile.java",
          "java/somefolder/perfect.java",
          "com/test/subtest/random.java"
        };
    ImmutableSortedSet<InputRule> inputs =
        InputRule.inputPathsAsInputRules(ImmutableSortedSet.copyOf(args));

    // Build rule that owns all inputs
    BuildTarget target1 = BuildTargetFactory.newInstance("//base/name1", "name1");
    BuildTarget target2 = BuildTargetFactory.newInstance("//base/name2", "name2");
    BuildRule owner1Rule = new StubBuildRule(target1, inputs);
    BuildRule owner2Rule = new StubBuildRule(target2, inputs);

    // Create graph
    MutableDirectedGraph<BuildRule> mutableGraph = new MutableDirectedGraph<BuildRule>();
    mutableGraph.addNode(owner1Rule);
    mutableGraph.addNode(owner2Rule);

    DependencyGraph graph = new DependencyGraph(mutableGraph);

    // Create options
    AuditOwnerOptions options = getOptions(args);

    // Create command under test
    AuditOwnerCommand command = createAuditOwnerCommand(filesystem);

    // Generate report and verify nonFileInputs are filled in as expected.
    AuditOwnerCommand.OwnersReport report = command.generateOwnersReport(graph, options);
    assertTrue(report.nonFileInputs.isEmpty());
    assertTrue(report.nonExistentInputs.isEmpty());
    assertTrue(report.inputsWithNoOwners.isEmpty());

    assertTrue(report.owners.containsKey(owner1Rule));
    assertTrue(report.owners.containsKey(owner2Rule));
    assertEquals(owner1Rule.getInputs(), report.owners.get(owner1Rule));
    assertEquals(owner2Rule.getInputs(), report.owners.get(owner2Rule));
  }
Exemple #5
0
  private PartialGraph createGraphFromBuildRules(List<BuildRule> rules) {
    MutableDirectedGraph<BuildRule> graph = new MutableDirectedGraph<>();
    for (BuildRule rule : rules) {
      for (BuildRule dep : rule.getDeps()) {
        graph.addEdge(rule, dep);
      }
    }

    List<BuildTarget> buildTargets =
        Lists.transform(
            rules,
            new Function<BuildRule, BuildTarget>() {
              @Override
              public BuildTarget apply(BuildRule rule) {
                return rule.getBuildTarget();
              }
            });

    DependencyGraph dependencyGraph = new DependencyGraph(graph);
    return PartialGraphFactory.newInstance(dependencyGraph, buildTargets);
  }
    /**
     * Walks the trie of paths attempting to merge all of the children of the current path into
     * itself. As soon as this fails we know we can't merge the parent path with the current path
     * either.
     *
     * @param currentPath current path
     * @return Optional.of(a successfully merged folder) or absent if merging did not succeed.
     */
    private Optional<IjFolder> walk(Path currentPath) {
      ImmutableList<Optional<IjFolder>> children =
          StreamSupport.stream(tree.getOutgoingNodesFor(currentPath).spliterator(), false)
              .map(this::walk)
              .collect(MoreCollectors.toImmutableList());

      List<IjFolder> presentChildren = new ArrayList<>(children.size());
      for (Optional<IjFolder> folderOptional : children) {
        if (!folderOptional.isPresent()) {
          return Optional.empty();
        }

        IjFolder folder = folderOptional.get();
        // We don't want to merge exclude folders.
        if (folder instanceof ExcludeFolder) {
          continue;
        }
        presentChildren.add(folderOptional.get());
      }

      IjFolder currentFolder = mergePathsMap.get(currentPath);
      if (presentChildren.isEmpty()) {
        return Optional.ofNullable(currentFolder);
      }

      final IjFolder mergeDistination;
      if (currentFolder != null) {
        mergeDistination = currentFolder;
      } else {
        mergeDistination = findBestChildToAggregateTo(presentChildren).createCopyWith(currentPath);
      }

      boolean allChildrenCanBeMerged =
          FluentIterable.from(presentChildren)
              .allMatch(input -> canMerge(mergeDistination, input, packagePathCache));
      if (!allChildrenCanBeMerged) {
        return Optional.empty();
      }

      return attemptMerge(mergeDistination, presentChildren);
    }
    /**
     * Walks the trie of paths attempting to merge all of the children of the current path into
     * itself. As soon as this fails we know we can't merge the parent path with the current path
     * either.
     *
     * @param currentPath current path
     * @return Optional.of(a successfully merged folder) or absent if merging did not succeed.
     */
    private Optional<IjFolder> walk(Path currentPath) {
      ImmutableList<Optional<IjFolder>> children =
          FluentIterable.from(tree.getOutgoingNodesFor(currentPath))
              .transform(
                  new Function<Path, Optional<IjFolder>>() {
                    @Override
                    public Optional<IjFolder> apply(Path input) {
                      return walk(input);
                    }
                  })
              .toList();

      boolean anyAbsent =
          FluentIterable.from(children).anyMatch(Predicates.equalTo(Optional.<IjFolder>absent()));
      if (anyAbsent) {
        // Signal that no further merging should be done.
        return Optional.absent();
      }

      ImmutableSet<IjFolder> presentChildren =
          FluentIterable.from(children)
              .transform(
                  new Function<Optional<IjFolder>, IjFolder>() {
                    @Override
                    public IjFolder apply(Optional<IjFolder> input) {
                      return input.get();
                    }
                  })
              .toSet();
      IjFolder currentFolder = mergePathsMap.get(currentPath);
      if (presentChildren.isEmpty()) {
        return Optional.of(Preconditions.checkNotNull(currentFolder));
      }

      final IjFolder myFolder;
      if (currentFolder != null) {
        myFolder = currentFolder;
      } else {
        IjFolder aChild = presentChildren.iterator().next();
        myFolder = aChild.createCopyWith(currentPath);
      }
      boolean allChildrenCanBeMerged =
          FluentIterable.from(presentChildren)
              .allMatch(
                  new Predicate<IjFolder>() {
                    @Override
                    public boolean apply(IjFolder input) {
                      return canMerge(myFolder, input, packagePathCache);
                    }
                  });
      if (!allChildrenCanBeMerged) {
        return Optional.absent();
      }
      IjFolder mergedFolder = myFolder;
      for (IjFolder presentChild : presentChildren) {
        mergePathsMap.remove(presentChild.getPath());
        mergedFolder = presentChild.merge(mergedFolder);
      }
      mergePathsMap.put(mergedFolder.getPath(), mergedFolder);

      return Optional.of(mergedFolder);
    }
 public ImmutableSet<IjFolder> getMergedFolders() {
   for (Path topLevel : tree.getNodesWithNoIncomingEdges()) {
     walk(topLevel);
   }
   return ImmutableSet.copyOf(mergePathsMap.values());
 }