Program: 6117
Project: 6117
Buggy source:
/** 
 * Create an execution plan for the given logical pipeline. This is used for batch pipelines. Though it may eventually be useful to mark windowing points for realtime pipelines. A plan consists of one or more phases, with connections between phases. A connection between a phase indicates control flow, and not necessarily data flow. This class assumes that it receives a valid pipeline spec. That is, the pipeline has no cycles, all its nodes have unique names, sources don't have any input, sinks don't have any output, everything else has both an input and an output, etc. We start by inserting connector nodes into the logical dag, which are used to mark boundaries between mapreduce jobs. Each connector represents a node where we will need to write to a local dataset. Next, the logical pipeline is broken up into phases, using the connectors as sinks in one phase, and a source in another. After this point, connections between phases do not indicate data flow, but control flow.
 * @param spec the pipeline spec, representing a logical pipeline
 * @return the execution plan
 */
public PipelinePlan plan(PipelineSpec spec){
  Set<String> reduceNodes=new HashSet<>();
  Set<String> isolationNodes=new HashSet<>();
  Set<String> actionNodes=new HashSet<>();
  Map<String,StageSpec> specs=new HashMap<>();
  for (  StageSpec stage : spec.getStages()) {
    if (reduceTypes.contains(stage.getPlugin().getType())) {
      reduceNodes.add(stage.getName());
    }
    if (isolationTypes.contains(stage.getPlugin().getType())) {
      isolationNodes.add(stage.getName());
    }
    if (Action.PLUGIN_TYPE.equals(stage.getPlugin().getType())) {
      actionNodes.add(stage.getName());
    }
    specs.put(stage.getName(),stage);
  }
  SetMultimap<String,String> outgoingActionConnections=HashMultimap.create();
  SetMultimap<String,String> incomingActionConnections=HashMultimap.create();
  Set<Connection> connectionsWithoutAction=new HashSet<>();
  for (  Connection connection : spec.getConnections()) {
    if (actionNodes.contains(connection.getFrom()) || actionNodes.contains(connection.getTo())) {
      if (actionNodes.contains(connection.getFrom())) {
        outgoingActionConnections.put(connection.getFrom(),connection.getTo());
      }
      if (actionNodes.contains(connection.getTo())) {
        incomingActionConnections.put(connection.getTo(),connection.getFrom());
      }
      continue;
    }
    connectionsWithoutAction.add(connection);
  }
  ConnectorDag cdag=ConnectorDag.builder().addConnections(connectionsWithoutAction).addReduceNodes(reduceNodes).addIsolationNodes(isolationNodes).build();
  cdag.insertConnectors();
  Set<String> connectorNodes=cdag.getConnectors();
  Map<String,Dag> subdags=new HashMap<>();
  for (  Dag subdag : cdag.splitOnConnectors()) {
    String name=getPhaseName(subdag.getSources(),subdag.getSinks());
    subdags.put(name,subdag);
  }
  Set<Connection> phaseConnections=new HashSet<>();
  for (  Map.Entry<String,Dag> subdagEntry1 : subdags.entrySet()) {
    String dag1Name=subdagEntry1.getKey();
    Dag dag1=subdagEntry1.getValue();
    for (    Map.Entry<String,Dag> subdagEntry2 : subdags.entrySet()) {
      String dag2Name=subdagEntry2.getKey();
      Dag dag2=subdagEntry2.getValue();
      if (dag1Name.equals(dag2Name)) {
        continue;
      }
      if (Sets.intersection(dag1.getSinks(),dag2.getSources()).size() > 0) {
        phaseConnections.add(new Connection(dag1Name,dag2Name));
      }
    }
  }
  Map<String,PipelinePhase> phases=new HashMap<>();
  for (  Map.Entry<String,Dag> dagEntry : subdags.entrySet()) {
    phases.put(dagEntry.getKey(),dagToPipeline(dagEntry.getValue(),connectorNodes,specs));
  }
  populateActionPhases(specs,actionNodes,phases,phaseConnections,outgoingActionConnections,incomingActionConnections,subdags);
  return new PipelinePlan(phases,phaseConnections);
}

Fixed version (ground truth):
/** 
 * Create an execution plan for the given logical pipeline. This is used for batch pipelines. Though it may eventually be useful to mark windowing points for realtime pipelines. A plan consists of one or more phases, with connections between phases. A connection between a phase indicates control flow, and not necessarily data flow. This class assumes that it receives a valid pipeline spec. That is, the pipeline has no cycles, all its nodes have unique names, sources don't have any input, sinks don't have any output, everything else has both an input and an output, etc. We start by inserting connector nodes into the logical dag, which are used to mark boundaries between mapreduce jobs. Each connector represents a node where we will need to write to a local dataset. Next, the logical pipeline is broken up into phases, using the connectors as sinks in one phase, and a source in another. After this point, connections between phases do not indicate data flow, but control flow.
 * @param spec the pipeline spec, representing a logical pipeline
 * @return the execution plan
 */
public PipelinePlan plan(PipelineSpec spec){
  Set<String> reduceNodes=new HashSet<>();
  Set<String> isolationNodes=new HashSet<>();
  Set<String> actionNodes=new HashSet<>();
  Map<String,StageSpec> specs=new HashMap<>();
  for (  StageSpec stage : spec.getStages()) {
    if (reduceTypes.contains(stage.getPlugin().getType())) {
      reduceNodes.add(stage.getName());
    }
    if (isolationTypes.contains(stage.getPlugin().getType())) {
      isolationNodes.add(stage.getName());
    }
    if (Action.PLUGIN_TYPE.equals(stage.getPlugin().getType())) {
      actionNodes.add(stage.getName());
    }
    specs.put(stage.getName(),stage);
  }
  SetMultimap<String,String> outgoingActionConnections=HashMultimap.create();
  SetMultimap<String,String> incomingActionConnections=HashMultimap.create();
  Set<Connection> connectionsWithoutAction=new HashSet<>();
  for (  Connection connection : spec.getConnections()) {
    if (actionNodes.contains(connection.getFrom()) || actionNodes.contains(connection.getTo())) {
      if (actionNodes.contains(connection.getFrom())) {
        outgoingActionConnections.put(connection.getFrom(),connection.getTo());
      }
      if (actionNodes.contains(connection.getTo())) {
        incomingActionConnections.put(connection.getTo(),connection.getFrom());
      }
      continue;
    }
    connectionsWithoutAction.add(connection);
  }
  ConnectorDag cdag=ConnectorDag.builder().addConnections(connectionsWithoutAction).addReduceNodes(reduceNodes).addIsolationNodes(isolationNodes).build();
  cdag.insertConnectors();
  Set<String> connectorNodes=cdag.getConnectors();
  Map<String,Dag> subdags=new HashMap<>();
  for (  Dag subdag : cdag.split()) {
    String name=getPhaseName(subdag.getSources(),subdag.getSinks());
    subdags.put(name,subdag);
  }
  Set<Connection> phaseConnections=new HashSet<>();
  for (  Map.Entry<String,Dag> subdagEntry1 : subdags.entrySet()) {
    String dag1Name=subdagEntry1.getKey();
    Dag dag1=subdagEntry1.getValue();
    for (    Map.Entry<String,Dag> subdagEntry2 : subdags.entrySet()) {
      String dag2Name=subdagEntry2.getKey();
      Dag dag2=subdagEntry2.getValue();
      if (dag1Name.equals(dag2Name)) {
        continue;
      }
      if (Sets.intersection(dag1.getSinks(),dag2.getSources()).size() > 0) {
        phaseConnections.add(new Connection(dag1Name,dag2Name));
      }
    }
  }
  Map<String,PipelinePhase> phases=new HashMap<>();
  for (  Map.Entry<String,Dag> dagEntry : subdags.entrySet()) {
    phases.put(dagEntry.getKey(),dagToPipeline(dagEntry.getValue(),connectorNodes,specs));
  }
  populateActionPhases(specs,actionNodes,phases,phaseConnections,outgoingActionConnections,incomingActionConnections,subdags);
  return new PipelinePlan(phases,phaseConnections);
}