Package de.danielbechler.diff.issues.issue15

Source Code of de.danielbechler.diff.issues.issue15.GraphIT

/*
* Copyright 2014 Daniel Bechler
*
* 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 de.danielbechler.diff.issues.issue15;

import de.danielbechler.diff.ObjectDiffer;
import de.danielbechler.diff.ObjectDifferBuilder;
import de.danielbechler.diff.circular.CircularReferenceDetector;
import de.danielbechler.diff.helper.NodeAssertions;
import de.danielbechler.diff.node.DiffNode;
import de.danielbechler.diff.node.NodeHierarchyVisitor;
import de.danielbechler.diff.node.PrintingVisitor;
import org.testng.annotations.Test;

import static de.danielbechler.diff.helper.NodeAssertions.assertThat;
import static de.danielbechler.diff.path.NodePath.startBuilding;
import static org.fest.assertions.api.Assertions.assertThat;

/**
* This tests have been provided by a user who had some trouble with circular relationships. The introduction
* of the {@link CircularReferenceDetector} fixed his problems. To avoid regression, these integration tests
* will be kept until other tests cover the same scenarios.
*
* @author https://github.com/oplohmann (original author)
* @author Daniel Bechler (modifications)
*/
public class GraphIT
{
  private static final boolean PRINT_ENABLED = true;

  @Test
  public void basicNode()
  {
    final GraphNode base = new GraphNode(1);
    final GraphNode a = new GraphNode(2, "a");
    base.setDirectReference(a);

    final GraphNode modified = new GraphNode(1);
    final GraphNode modifiedA = new GraphNode(2, "ax");
    modified.setDirectReference(modifiedA);

    final DiffNode root = compareAndPrint(modified, base);

    assertThat(root).root().hasChildren(1);
    assertThat(root).child("directReference", "value").hasState(DiffNode.State.CHANGED).hasNoChildren();
  }

  private static DiffNode compareAndPrint(final GraphNode modified, final GraphNode base)
  {
    final DiffNode root = ObjectDifferBuilder.buildDefault().compare(modified, base);
    if (PRINT_ENABLED)
    {
      root.visit(new PrintingVisitor(modified, base));
    }
    return root;
  }

  @Test
  public void basicNodeWithDirectReferences()
  {
    final GraphNode base = new GraphNode(1);
    final GraphNode a = new GraphNode(2, "a");
    base.setDirectReference(a);

    final GraphNode modified = new GraphNode(1);
    final GraphNode modifiedA = new GraphNode(2, "ax");
    modified.setDirectReference(modifiedA);

    final DiffNode root = compareAndPrint(modified, base);

    assertThat(root).root().hasChildren(1);
    assertThat(root).child("directReference", "value").hasState(DiffNode.State.CHANGED).hasNoChildren();
  }

  @Test
  public void basicBidirectionalWithChildren()
  {
    final GraphNode base = new GraphNode(1);
    final GraphNode a = new GraphNode(2, "a");
    final GraphNode b = new GraphNode(3, "b");
    base.addChildren(a, b);
    establishCircularDirectReference(a, b);

    final GraphNode modified = new GraphNode(1);
    final GraphNode modifiedA = new GraphNode(2, "ax");
    final GraphNode modifiedB = new GraphNode(3, "by");
    modified.addChildren(modifiedA, modifiedB);
    establishCircularDirectReference(modifiedA, modifiedB);

    final DiffNode root = compareAndPrint(modified, base);

    assertThat(root).root().hasChildren(1);
    assertThat(root).child("children").hasChildren(2);
    assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(a))
        .hasState(DiffNode.State.CHANGED);
    assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(b))
        .hasState(DiffNode.State.CHANGED);
  }

  private static void establishCircularDirectReference(final GraphNode a, final GraphNode b)
  {
    a.setDirectReference(b);
    b.setDirectReference(a);
  }

  @Test //(timeout = 1000)
  public void basicBidirectionalWithChildrenAndMaps()
  {
    final GraphNode a = new GraphNode(2, "a");
    final GraphNode b = new GraphNode(3, "b");
    establishCircularDirectReference(a, b);
    a.getMap().put("node-b", b);
    a.getMap().put("node-b-again", b);
    b.getMap().put("node-a", a);

    final GraphNode base = new GraphNode(1);
    base.getMap().put("a", "a");
    base.getChildren().add(b);

    final GraphNode modified = new GraphNode(1);
    final GraphNode modifiedA = new GraphNode(2, "ax");
    modified.getMap().put("a", "az");
    final GraphNode modifiedB = new GraphNode(3, "by");
    modified.addChild(modifiedB);
    establishCircularDirectReference(modifiedA, modifiedB);
    modifiedA.getMap().put("node-b", modifiedB);
    modifiedB.getMap().put("node-a", modifiedA);

    final DiffNode root = compareAndPrint(modified, base);

    /*
     * one could assume that this node would be marked as removed, but since the instance it represents
     * is a parent of it, we cannot reliably test, whether the node has been changed. (added or removed
     * is easier, but since changed is not yet possible, that would be inconsistent)
     */
    assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(b)
        .propertyName("directReference")
        .propertyName("map")
        .mapKey("node-b-again"))
        .hasState(DiffNode.State.CIRCULAR); // TODO Change circular reference detection, to allow returning the expected "REMOVED" state

    assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(b)
        .propertyName("directReference")
        .propertyName("value")).hasState(DiffNode.State.CHANGED);

    assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(b)
        .propertyName("map")
        .mapKey("node-a")
        .propertyName("map")
        .mapKey("node-b-again"))
        .hasState(DiffNode.State.CIRCULAR); // TODO Change circular reference detection, to allow returning the expected "REMOVED" state

    assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(b)
        .propertyName("map")
        .mapKey("node-a")
        .propertyName("value")).hasState(DiffNode.State.CHANGED);

    assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(b)
        .propertyName("value")).hasState(DiffNode.State.CHANGED);

    assertThat(root).child(startBuilding()
        .propertyName("map")
        .mapKey("a")).hasState(DiffNode.State.CHANGED);
  }

  @Test
  public void basicBidirectionalWithoutChildren()
  {
    final GraphNode a = new GraphNode(1, "a");
    final GraphNode b = new GraphNode(2, "b");
    establishCircularDirectReference(a, b);

    final GraphNode modifiedA = new GraphNode(1, "ax");
    final GraphNode modifiedB = new GraphNode(2, "by");
    establishCircularDirectReference(modifiedA, modifiedB);

    final DiffNode root = compareAndPrint(modifiedA, a);

    assertThat(root).child("directReference", "value").hasState(DiffNode.State.CHANGED).hasNoChildren();
    assertThat(root).child("value").hasState(DiffNode.State.CHANGED).hasNoChildren();
  }

  @Test
  public void basicNodeWithDirectReferences2()
  {
    final GraphNode base = new GraphNode("base");
    final GraphNode a = new GraphNode("a");
    base.setDirectReference(a);

    final GraphNode modified = new GraphNode("modified");
    final GraphNode modifiedA = new GraphNode("ax");
    modified.setDirectReference(modifiedA);

    final DiffNode root = compareAndPrint(modified, base);

    assertThat(root).child("directReference", "value").hasState(DiffNode.State.CHANGED).hasNoChildren();
    assertThat(root).child("value").hasState(DiffNode.State.CHANGED).hasNoChildren();
  }

  /**
   * Does not detect any changes since no primary key defined for each node
   */
  @Test
  public void basicBidirectionalNodeWithChildNodes()
  {
    final GraphNode base = new GraphNode();
    final GraphNode a = new GraphNode("a");
    final GraphNode b = new GraphNode("b");
    base.getChildren().add(a);
    base.getChildren().add(b);

    final GraphNode modified = new GraphNode();
    final GraphNode modifiedA = new GraphNode("a");
    final GraphNode modifiedB = new GraphNode("bx");
    modified.getChildren().add(modifiedA);
    modified.getChildren().add(modifiedB);

    final DiffNode root = compareAndPrint(modified, base);

    NodeAssertions.assertThat(root).root().hasState(DiffNode.State.UNTOUCHED); // not a bug!

    // NOTE: This is expected, since Collections (and java-object-diff) rely heavily on the proper
    // implementation of hashCode and equals. The GraphNode uses the ID as sole criteria in it's
    // equality check. Since all instances created without an explicit ID, will default to -1, all
    // GraphNodes in this example are technically equal.
    //
    // The following test proves this:

    final GraphNode actualA = base.getChildren().get(0);
    final GraphNode actualB = base.getChildren().get(1);
    assertThat(actualA).isEqualTo(a);
    assertThat(actualB).isEqualTo(b);
    assertThat(actualA).isEqualTo(actualB);
  }

  @Test
  public void basicBidirectionalNodeWithChildNodesWithIds()
  {
    final GraphNode a = new GraphNode(2, "a");
    final GraphNode b = new GraphNode(3, "b");
    final GraphNode base = new GraphNode(1);
    base.addChild(a);
    base.addChild(b);

    final GraphNode modifiedA = new GraphNode(2, "ax");
    final GraphNode modifiedB = new GraphNode(3, "by");
    final GraphNode modified = new GraphNode(1);
    modified.addChild(modifiedA);
    modified.addChild(modifiedB);

    final DiffNode root = compareAndPrint(modified, base);

    NodeAssertions.assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(a))
        .hasState(DiffNode.State.CHANGED)
        .hasChildren(1);

    NodeAssertions.assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(b))
        .hasState(DiffNode.State.CHANGED)
        .hasChildren(1);
  }

  @Test
  public void simpleGraphWithChanges()
  {
    final GraphNode base = new GraphNode(1);
    final GraphNode a = new GraphNode(2, "a");
    establishParentChildRelationship(base, a);
    final GraphNode modified = new GraphNode(1);
    final GraphNode modifiedA = new GraphNode(2, "ax");
    establishParentChildRelationship(modified, modifiedA);

    final DiffNode root = compareAndPrint(modified, base);

    NodeAssertions.assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(modifiedA)
        .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);
  }

  private static void establishParentChildRelationship(final GraphNode parent, final GraphNode child)
  {
    child.setParent(parent);
    parent.addChild(child);
  }

  @Test
  public void simpleGraphWithoutChanges()
  {
    final GraphNode base = new GraphNode(1);
    final GraphNode a = new GraphNode(2, "a");
    establishParentChildRelationship(base, a);
    final GraphNode modified = new GraphNode(1);
    final GraphNode modifiedA = new GraphNode(2, "a");
    establishParentChildRelationship(modified, modifiedA);

    final DiffNode root = compareAndPrint(modified, base);

    assertThat(root).root().hasState(DiffNode.State.UNTOUCHED);
  }

  @Test
  public void simpleGraphExtended()
  {
    final GraphNode a = new GraphNode(2, "a");
    final GraphNode b = new GraphNode(3, "b");
    final GraphNode base = new GraphNode(1);
    establishParentChildRelationship(base, a);
    establishParentChildRelationship(base, b);

    final GraphNode modifiedA = new GraphNode(2, "a");
    final GraphNode modifiedB = new GraphNode(3, "bx");
    final GraphNode modified = new GraphNode(1);
    establishParentChildRelationship(modified, modifiedA);
    establishParentChildRelationship(modified, modifiedB);

    final DiffNode root = compareAndPrint(modified, base);

    assertThat(root).child(startBuilding()
        .propertyName("children")
        .collectionItem(b)
        .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);
  }

  @Test
  public void bidirectionalGraphStackOverflow()
  {
    final GraphNode a = new GraphNode(2, "a");
    final GraphNode b = new GraphNode(3, "b");
    establishCircularDirectReference(a, b);

    final GraphNode base = new GraphNode(1);
    establishParentChildRelationship(base, a);
    establishParentChildRelationship(base, b);

    final GraphNode aa = new GraphNode(4, "aa");
    final GraphNode ba = new GraphNode(5, "ba");
    establishCircularChildRelationship(aa, ba);

    establishParentChildRelationship(a, aa);
    establishParentChildRelationship(b, ba);

    final GraphNode baa = new GraphNode(6, "baa");
    establishParentChildRelationship(ba, baa);

    final GraphNode modifiedA = new GraphNode(2, "a");
    final GraphNode modifiedB = new GraphNode(3, "b");
    establishCircularDirectReference(modifiedA, modifiedB);

    final GraphNode modified = new GraphNode(1);
    establishParentChildRelationship(modified, modifiedA);
    establishParentChildRelationship(modified, modifiedB);

    final GraphNode modifiedAA = new GraphNode(4, "aa");
    final GraphNode modifiedBA = new GraphNode(5, "ba-x");
    establishCircularChildRelationship(modifiedAA, modifiedBA);

    establishParentChildRelationship(modifiedA, modifiedAA);
    establishParentChildRelationship(modifiedB, modifiedBA);

    final GraphNode modifieBAA = new GraphNode(6, "baa-y");
    establishParentChildRelationship(modifiedBA, modifieBAA);

    final DiffNode root = compareAndPrint(modified, base);

    NodeAssertions.assertThat(root)
        .child(startBuilding()
            .propertyName("children")
            .collectionItem(a)
            .propertyName("children")
            .collectionItem(aa)
            .propertyName("children")
            .collectionItem(ba)
            .propertyName("children")
            .collectionItem(baa))
        .hasState(DiffNode.State.CHANGED);

    NodeAssertions.assertThat(root)
        .child(startBuilding()
            .propertyName("children")
            .collectionItem(a)
            .propertyName("children")
            .collectionItem(aa)
            .propertyName("children")
            .collectionItem(ba)
            .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);

    NodeAssertions.assertThat(root)
        .child(startBuilding()
            .propertyName("children")
            .collectionItem(a)
            .propertyName("directReference")
            .propertyName("children")
            .collectionItem(ba)
            .propertyName("children")
            .collectionItem(baa)
            .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);

    NodeAssertions.assertThat(root)
        .child(startBuilding()
            .propertyName("children")
            .collectionItem(a)
            .propertyName("directReference")
            .propertyName("children")
            .collectionItem(ba)
            .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);

    NodeAssertions.assertThat(root)
        .child(startBuilding()
            .propertyName("children")
            .collectionItem(b)
            .propertyName("children")
            .collectionItem(ba)
            .propertyName("children")
            .collectionItem(baa)
            .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);

    NodeAssertions.assertThat(root)
        .child(startBuilding()
            .propertyName("children")
            .collectionItem(b)
            .propertyName("children")
            .collectionItem(ba)
            .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);

    NodeAssertions.assertThat(root)
        .child(startBuilding()
            .propertyName("children")
            .collectionItem(b)
            .propertyName("directReference")
            .propertyName("children")
            .collectionItem(aa)
            .propertyName("children")
            .collectionItem(ba)
            .propertyName("children")
            .collectionItem(baa)
            .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);

    NodeAssertions.assertThat(root)
        .child(startBuilding()
            .propertyName("children")
            .collectionItem(b)
            .propertyName("directReference")
            .propertyName("children")
            .collectionItem(aa)
            .propertyName("children")
            .collectionItem(ba)
            .propertyName("value"))
        .hasState(DiffNode.State.CHANGED);
  }

  private static void establishCircularChildRelationship(final GraphNode a, final GraphNode b)
  {
    a.addChild(b);
    b.addChild(a);
  }

  @Test
  public void testWithSimpleBiDirectionalConnection()
  {
    final GraphNode working1 = new GraphNode(1, "foo");
    final GraphNode working2 = new GraphNode(2, "bar");
    establishCircularDirectReference(working1, working2);

    final GraphNode base1 = new GraphNode(1);
    final GraphNode base2 = new GraphNode(2);
    establishCircularDirectReference(base1, base2);

    final ObjectDifferBuilder objectDifferBuilder = ObjectDifferBuilder.startBuilding();
    objectDifferBuilder.filtering().returnNodesWithState(DiffNode.State.CIRCULAR);
    final DiffNode node = objectDifferBuilder.build().compare(working1, base1);
    node.visit(new NodeHierarchyVisitor());

    assertThat(node).child("value").hasState(DiffNode.State.ADDED);
    assertThat(node).child("directReference").hasState(DiffNode.State.CHANGED);
    assertThat(node).child("directReference", "value").hasState(DiffNode.State.ADDED);
    assertThat(node).child("directReference", "directReference").isCircular();
  }

  @Test
  public void testWithMapBasedBiDirectionalConnection()
  {
    final GraphNode working1 = new GraphNode(1, "foo");
    final GraphNode working2 = new GraphNode(2, "bar");
    working1.getMap().put("foo", working2);
    working2.getMap().put("bar", working1);

    final GraphNode base1 = new GraphNode(1);
    final GraphNode base2 = new GraphNode(2);
    base1.getMap().put("foo", base2);
    base2.getMap().put("bar", base1);

    final ObjectDifferBuilder objectDifferBuilder = ObjectDifferBuilder.startBuilding();
    objectDifferBuilder.filtering().returnNodesWithState(DiffNode.State.CIRCULAR);
    final ObjectDiffer differ = objectDifferBuilder.build();

    final DiffNode node = differ.compare(working1, base1);
    node.visit(new NodeHierarchyVisitor());

    NodeAssertions.assertThat(node)
        .child(startBuilding()
            .propertyName("map")
            .mapKey("foo")
            .propertyName("map")
            .mapKey("bar"))
        .isCircular();
  }
}
TOP

Related Classes of de.danielbechler.diff.issues.issue15.GraphIT

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.