/*
 * 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.graph;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import com.google.common.collect.ImmutableSet;

import org.junit.Test;

public class MutableDirectedGraphTest {

  @Test
  public void testCreateNewGraph() {
    MutableDirectedGraph<String> graph = new MutableDirectedGraph<String>();
    assertEquals(0, graph.getNodeCount());
    assertEquals(0, graph.getEdgeCount());
    assertFalse(graph.containsNode("A"));
    assertFalse(graph.containsEdge("A", "B"));
    assertTrue(graph.isAcyclic());
  }

  @Test
  public void testCreateGraphWithOneEdge() {
    MutableDirectedGraph<String> graph = new MutableDirectedGraph<String>();
    graph.addEdge("A", "B");
    assertEquals(2, graph.getNodeCount());
    assertEquals(1, graph.getEdgeCount());
    assertTrue(graph.containsEdge("A", "B"));
    assertEquals(ImmutableSet.of("A"), graph.getNodesWithNoIncomingEdges());
    assertTrue(graph.isAcyclic());
  }

  /**
   * Make sure a node can point to itself. Also ensure that when a self-referential edge is removed,
   * the graph is left in the correct state.
   */
  @Test
  public void testAddSelfReferentialEdge() {
    MutableDirectedGraph<String> graph = new MutableDirectedGraph<String>();
    graph.addEdge("A", "A");
    assertEquals(1, graph.getNodeCount());
    assertEquals(1, graph.getEdgeCount());
    assertTrue(graph.containsNode("A"));
    assertTrue(graph.containsEdge("A", "A"));
    assertEquals(ImmutableSet.of("A"), graph.getOutgoingNodesFor("A"));
    assertEquals(ImmutableSet.of("A"), graph.getIncomingNodesFor("A"));
    assertTrue(graph.hasIncomingEdges("A"));
    assertFalse(graph.isAcyclic());
    assertEquals(ImmutableSet.of(ImmutableSet.of("A")), graph.findCycles());

    graph.removeNode("A");
    assertEquals(0, graph.getNodeCount());
    assertEquals(0, graph.getEdgeCount());
    assertFalse(graph.containsNode("A"));
    assertFalse(graph.containsEdge("A", "A"));
    assertEquals(ImmutableSet.of(), graph.getOutgoingNodesFor("A"));
    assertEquals(ImmutableSet.of(), graph.getIncomingNodesFor("A"));
    assertTrue(graph.isAcyclic());
  }

  @Test
  public void testIsAcyclic() {
    MutableDirectedGraph<String> graph = new MutableDirectedGraph<String>();
    graph.addEdge("A", "B");
    assertTrue(graph.isAcyclic());
    graph.addEdge("B", "C");
    assertTrue(graph.isAcyclic());
    graph.addEdge("C", "A");
    graph.addEdge("D", "E");
    assertFalse("Graph has a cycle: A->B->C", graph.isAcyclic());
    assertEquals(ImmutableSet.of(ImmutableSet.of("A", "B", "C")), graph.findCycles());

    graph.removeEdge("C", "A");
    assertTrue(graph.isAcyclic());
    graph.addEdge("A", "D");
    assertTrue(graph.isAcyclic());
    graph.addEdge("D", "C");
    assertTrue(graph.isAcyclic());
  }

  @Test
  public void testFindCycles() {
    MutableDirectedGraph<String> graph = new MutableDirectedGraph<String>();
    graph.addEdge("A", "B");
    graph.addEdge("B", "C");
    graph.addEdge("C", "A");
    graph.addEdge("D", "E");
    graph.addEdge("E", "D");
    graph.addEdge("F", "F");
    assertEquals(
        ImmutableSet.of(
            ImmutableSet.of("A", "B", "C"),
            ImmutableSet.of("D", "E"),
            ImmutableSet.of("F")),
        graph.findCycles());
  }

  @Test
  public void testTrivialDisconnectedGraphIsAcyclic() {
    MutableDirectedGraph<String> graph = new MutableDirectedGraph<String>();
    graph.addNode("A");
    graph.addNode("B");
    graph.addNode("C");
    assertTrue(graph.isAcyclic());
  }
}

@@ -839,16 +839,39 @@
 .Test;%0A%0A
+import java.util.Set;%0A%0A
 public c
@@ -1227,32 +1227,87 @@
 h.isAcyclic());%0A
+    assertEquals(ImmutableSet.of(), graph.getNodes());%0A
   %7D%0A%0A  @Test%0A  p
@@ -1417,32 +1417,126 @@
 raph%3CString%3E();%0A
+    Set%3CString%3E nodesView = graph.getNodes();%0A    assertEquals(ImmutableSet.of(), nodesView);%0A
     graph.addEdg
@@ -1753,24 +1753,24 @@
 ngEdges());%0A
-
     assertTr
@@ -1788,24 +1788,80 @@
 Acyclic());%0A
+    assertEquals(ImmutableSet.of(%22A%22, %22B%22), nodesView);%0A
   %7D%0A%0A  /**%0A 
@@ -4421,11 +4421,476 @@
 ());%0A  %7D
+%0A%0A  @Test%0A  public void testCopyConstructor() %7B%0A    MutableDirectedGraph%3CString%3E originalGraph = new MutableDirectedGraph%3C%3E();%0A    originalGraph.addEdge(%22A%22, %22B%22);%0A%0A    MutableDirectedGraph%3CString%3E copyOfGraph = new MutableDirectedGraph%3C%3E(originalGraph);%0A    assertEquals(ImmutableSet.of(%22A%22, %22B%22), copyOfGraph.getNodes());%0A%0A    originalGraph.addEdge(%22B%22, %22C%22);%0A    assertEquals(2, originalGraph.getEdgeCount());%0A    assertEquals(1, copyOfGraph.getEdgeCount());%0A  %7D
 %0A%7D%0A

Add more test coverage for MutableDirectedGraph.