public class SMGJoinTargetObjectsTest {
  private SMG smg1;
  private SMG smg2;
  private SMG destSMG;

  private SMGNodeMapping mapping1;
  private SMGNodeMapping mapping2;

  private final SMGObject obj1 = new SMGRegion(8, "ze label");
  private final Integer value1 = SMGValueFactory.getNewValue();
  private final SMGEdgePointsTo pt1 = new SMGEdgePointsTo(value1, obj1, 0);

  private final SMGObject obj2 = new SMGRegion(8, "ze label");
  private final Integer value2 = SMGValueFactory.getNewValue();
  private final SMGEdgePointsTo pt2 = new SMGEdgePointsTo(value2, obj2, 0);

  private final SMGObject destObj = new SMGRegion(8, "destination");

  @Before
  public void setUp() {
    smg1 = new SMG(MachineModel.LINUX64);
    smg2 = new SMG(MachineModel.LINUX64);
    destSMG = new SMG(MachineModel.LINUX64);

    mapping1 = new SMGNodeMapping();
    mapping2 = new SMGNodeMapping();
  }

  @Test
  public void matchingObjectsWithoutMappingTest() throws SMGInconsistentException {
    smg1.addObject(obj1);
    smg1.addValue(value1);
    smg1.addPointsToEdge(pt1);

    smg2.addObject(obj2);
    smg2.addValue(value2);
    smg2.addPointsToEdge(pt2);

    SMGJoinTargetObjects jto =
        new SMGJoinTargetObjects(
            SMGJoinStatus.EQUAL,
            smg1,
            smg2,
            destSMG,
            mapping1,
            mapping2,
            SMGLevelMapping.createDefaultLevelMap(),
            value1,
            value2,
            0,
            0,
            0,
            false,
            null,
            null);
    Assert.assertSame(jto.getMapping1().get(obj1), jto.getMapping2().get(obj2));
    // TODO investigate why they should not be the same, regions are immutable
    // Assert.assertNotSame(jto.getMapping1().get(obj1), obj1);
    Assert.assertTrue(((SMGRegion) jto.getMapping1().get(obj1)).propertiesEqual((SMGRegion) obj1));
  }

  @Test
  public void nonMatchingObjectsTest() throws SMGInconsistentException {
    smg1.addObject(obj1);
    smg1.addValue(value1);
    smg1.addPointsToEdge(pt1);

    SMGJoinMatchObjects mo =
        new SMGJoinMatchObjects(
            SMGJoinStatus.EQUAL, smg1, smg2, mapping1, mapping2, obj1, smg2.getNullObject());
    Assert.assertFalse(mo.isDefined());
    SMGJoinTargetObjects jto =
        new SMGJoinTargetObjects(
            SMGJoinStatus.EQUAL,
            smg1,
            smg2,
            destSMG,
            mapping1,
            mapping2,
            SMGLevelMapping.createDefaultLevelMap(),
            value1,
            smg2.getNullValue(),
            0,
            0,
            0,
            false,
            null,
            null);
    Assert.assertFalse(jto.isDefined());
    Assert.assertTrue(jto.isRecoverable());
  }

  @Test
  public void joinTargetObjectsDifferentOffsets() throws SMGInconsistentException {
    SMGEdgePointsTo pt1null = new SMGEdgePointsTo(value1, smg1.getNullObject(), 2);
    SMGEdgePointsTo pt2null = new SMGEdgePointsTo(value2, smg2.getNullObject(), 1);

    smg1.addObject(obj1);
    smg1.addValue(value1);
    smg1.addPointsToEdge(pt1null);

    smg2.addObject(obj2);
    smg2.addValue(value2);
    smg2.addPointsToEdge(pt2null);

    SMGJoinTargetObjects jto =
        new SMGJoinTargetObjects(
            SMGJoinStatus.EQUAL,
            smg1,
            smg2,
            null,
            null,
            null,
            SMGLevelMapping.createDefaultLevelMap(),
            value1,
            value2,
            0,
            0,
            0,
            false,
            null,
            null);

    Assert.assertFalse(jto.isDefined());
    Assert.assertTrue(jto.isRecoverable());
  }

  @Test
  public void joinTargetObjectsAlreadyJoinedNull() throws SMGInconsistentException {
    SMGEdgePointsTo pt1null = new SMGEdgePointsTo(value1, smg1.getNullObject(), 0);
    SMGEdgePointsTo pt2null = new SMGEdgePointsTo(value2, smg2.getNullObject(), 0);

    smg1.addValue(value1);
    smg2.addValue(value2);

    smg1.addPointsToEdge(pt1null);
    smg2.addPointsToEdge(pt2null);

    SMGJoinMapTargetAddress mta =
        new SMGJoinMapTargetAddress(
            new SMG(smg1),
            new SMG(smg2),
            new SMG(destSMG),
            new SMGNodeMapping(mapping1),
            new SMGNodeMapping(mapping2),
            value1,
            value2);
    SMGJoinTargetObjects jto =
        new SMGJoinTargetObjects(
            SMGJoinStatus.EQUAL,
            smg1,
            smg2,
            destSMG,
            mapping1,
            mapping2,
            SMGLevelMapping.createDefaultLevelMap(),
            value1,
            value2,
            0,
            0,
            0,
            false,
            null,
            null);
    Assert.assertTrue(jto.isDefined());
    Assert.assertEquals(SMGJoinStatus.EQUAL, jto.getStatus());
    Assert.assertSame(smg1, jto.getInputSMG1());
    Assert.assertSame(smg2, jto.getInputSMG2());
    Assert.assertEquals(mta.getSMG(), jto.getDestinationSMG());
    Assert.assertEquals(mta.getMapping1(), jto.getMapping1());
    Assert.assertEquals(mta.getMapping2(), jto.getMapping2());
    Assert.assertEquals(mta.getValue(), jto.getValue());
  }

  @Test
  public void joinTargetObjectsAlreadyJoinedNonNull() throws SMGInconsistentException {
    smg1.addValue(value1);
    smg2.addValue(value2);

    smg1.addObject(obj1);
    smg2.addObject(obj2);
    destSMG.addObject(destObj);

    smg1.addPointsToEdge(pt1);
    smg2.addPointsToEdge(pt2);

    mapping1.map(obj1, destObj);
    mapping2.map(obj2, destObj);

    // See TODO below
    // SMGMapTargetAddress mta = new SMGMapTargetAddress(new SMG(smg1), new SMG(smg2), new
    // SMG(destSMG),
    //                                                  new SMGNodeMapping(mapping1), new
    // SMGNodeMapping(mapping2),
    //                                                  value1, value2);
    SMGJoinTargetObjects jto =
        new SMGJoinTargetObjects(
            SMGJoinStatus.EQUAL,
            smg1,
            smg2,
            destSMG,
            mapping1,
            mapping2,
            SMGLevelMapping.createDefaultLevelMap(),
            value1,
            value2,
            0,
            0,
            0,
            false,
            null,
            null);
    Assert.assertTrue(jto.isDefined());
    Assert.assertEquals(SMGJoinStatus.EQUAL, jto.getStatus());
    Assert.assertSame(smg1, jto.getInputSMG1());
    Assert.assertSame(smg2, jto.getInputSMG2());
    // TODO: Not equal, but isomorphic (newly created values differ in mta and jto)
    //       But we currently do not have isomorphism
    // Assert.assertEquals(mta.getSMG(), jto.getDestinationSMG());

    Assert.assertTrue(jto.getMapping1().containsKey(value1));
    Assert.assertEquals(jto.getMapping1().get(value1), jto.getValue());

    Assert.assertTrue(jto.getMapping2().containsKey(value2));
    Assert.assertEquals(jto.getMapping2().get(value2), jto.getValue());
  }
}
  @Test
  public void listWithInboundPointersTest() {
    CLangSMG smg = new CLangSMG(MachineModel.LINUX64);
    Integer tail = TestHelpers.createList(smg, 4, 16, 8, "tail");

    SMGEdgeHasValue head = TestHelpers.createGlobalList(smg, 3, 16, 8, "head");

    SMGObject inside = new SMGRegion(16, "pointed_at");
    SMGEdgeHasValue tailConnection =
        new SMGEdgeHasValue(AnonymousTypes.dummyPointer, 8, inside, tail);

    Integer addressOfInside = SMGValueFactory.getNewValue();
    SMGEdgePointsTo insidePT = new SMGEdgePointsTo(addressOfInside, inside, 0);
    SMGRegion inboundPointer = new SMGRegion(8, "inbound_pointer");
    SMGEdgeHasValue inboundPointerConnection =
        new SMGEdgeHasValue(AnonymousTypes.dummyPointer, 0, inboundPointer, addressOfInside);

    SMGObject lastFromHead = smg.getPointer(head.getValue()).getObject();
    SMGEdgeHasValue connection = null;
    do {
      SMGEdgeHasValueFilter filter =
          SMGEdgeHasValueFilter.objectFilter(lastFromHead).filterAtOffset(8);
      Set<SMGEdgeHasValue> connections = smg.getHVEdges(filter);
      connection = null;
      if (connections.size() > 0) {
        connection = Iterables.getOnlyElement(connections);
        lastFromHead = smg.getPointer(connection.getValue()).getObject();
      }
    } while (connection != null);

    for (SMGEdgeHasValue hv : smg.getHVEdges(SMGEdgeHasValueFilter.objectFilter(lastFromHead))) {
      smg.removeHasValueEdge(hv);
    }

    SMGEdgeHasValue headConnection =
        new SMGEdgeHasValue(AnonymousTypes.dummyPointer, 8, lastFromHead, addressOfInside);

    SMGRegion tailPointer = new SMGRegion(8, "tail_pointer");
    SMGEdgeHasValue tailPointerConnection =
        new SMGEdgeHasValue(AnonymousTypes.dummyPointer, 0, tailPointer, tail);

    smg.addGlobalObject(tailPointer);
    smg.addHasValueEdge(tailPointerConnection);

    smg.addHeapObject(inside);
    smg.addValue(addressOfInside);
    smg.addPointsToEdge(insidePT);

    smg.addGlobalObject(inboundPointer);
    smg.addHasValueEdge(inboundPointerConnection);

    smg.addHasValueEdge(tailConnection);
    smg.addHasValueEdge(headConnection);

    SMGSingleLinkedListFinder finder = new SMGSingleLinkedListFinder(1);
    Set<SMGAbstractionCandidate> candidates = finder.traverse(smg);
    Assert.assertEquals(2, candidates.size());

    boolean sawHead = false;
    boolean sawTail = false;
    for (SMGAbstractionCandidate candidate : candidates) {
      SMGSingleLinkedListCandidate sllCandidate = (SMGSingleLinkedListCandidate) candidate;
      if (sllCandidate.getLength() == 3) {
        Assert.assertSame(smg.getPointer(head.getValue()).getObject(), sllCandidate.getStart());
        Assert.assertFalse(sawHead);
        sawHead = true;
      } else if (sllCandidate.getLength() == 4) {
        Assert.assertSame(smg.getPointer(tail).getObject(), sllCandidate.getStart());
        Assert.assertFalse(sawTail);
      } else {
        Assert.fail("We should not see any candidates with length other than 3 or 4");
      }
    }
  }
public class SMGJoinMapTargetAddressTest {

  private SMG smg1;
  private SMG destSMG;
  private SMGNodeMapping mapping1;
  private SMGNodeMapping mapping2;

  final SMGRegion obj1 = new SMGRegion(8, "ze label");
  final Integer value1 = SMGValueFactory.getNewValue();
  final SMGEdgePointsTo edge1 = new SMGEdgePointsTo(value1, obj1, 0);

  final Integer value2 = SMGValueFactory.getNewValue();

  final SMGObject destObj = new SMGRegion(8, "destination");
  final Integer destValue = SMGValueFactory.getNewValue();

  @Before
  public void setUp() {
    smg1 = new SMG(MachineModel.LINUX64);
    destSMG = new SMG(MachineModel.LINUX64);
    mapping1 = new SMGNodeMapping();
    mapping2 = new SMGNodeMapping();
  }

  @Test
  public void mapTargetAddressExistingNull() {
    SMG origDestSMG = new SMG(destSMG);
    SMGNodeMapping origMapping1 = new SMGNodeMapping(mapping1);

    SMGJoinMapTargetAddress mta =
        new SMGJoinMapTargetAddress(smg1, null, destSMG, mapping1, null, smg1.getNullValue(), null);
    Assert.assertEquals(origDestSMG, mta.getSMG());
    Assert.assertEquals(origMapping1, mta.getMapping1());
    Assert.assertNull(mta.getMapping2());
    Assert.assertSame(destSMG.getNullValue(), mta.getValue());
  }

  @Test
  public void mapTargetAddressExisting() {
    SMGEdgePointsTo destEdge = new SMGEdgePointsTo(destValue, destObj, 0);

    smg1.addValue(value1);
    smg1.addObject(obj1);
    smg1.addPointsToEdge(edge1);

    destSMG.addValue(destValue);
    destSMG.addObject(destObj);
    destSMG.addPointsToEdge(destEdge);

    mapping1.map(obj1, destObj);

    SMGNodeMapping origMapping1 = new SMGNodeMapping(mapping1);
    SMG origDestSMG = new SMG(destSMG);

    SMGJoinMapTargetAddress mta =
        new SMGJoinMapTargetAddress(smg1, null, destSMG, mapping1, null, value1, null);
    Assert.assertEquals(origDestSMG, mta.getSMG());
    Assert.assertEquals(origMapping1, mta.getMapping1());
    Assert.assertNull(mta.getMapping2());
    Assert.assertSame(destValue, mta.getValue());
  }

  @Test
  public void mapTargetAddressNew() {
    smg1.addValue(value1);
    smg1.addObject(obj1);
    smg1.addPointsToEdge(edge1);

    destSMG.addObject(destObj);

    mapping1.map(obj1, destObj);

    SMGNodeMapping origMapping1 = new SMGNodeMapping(mapping1);
    SMGNodeMapping origMapping2 = new SMGNodeMapping(mapping2);
    SMG origDestSMG = new SMG(destSMG);

    SMGJoinMapTargetAddress mta =
        new SMGJoinMapTargetAddress(smg1, null, destSMG, mapping1, mapping2, value1, value2);
    Assert.assertNotEquals(origDestSMG, mta.getSMG());
    Assert.assertNotEquals(origMapping1, mta.getMapping1());
    Assert.assertNotEquals(origMapping2, mta.getMapping2());

    Assert.assertFalse(origDestSMG.getValues().contains(mta.getValue()));

    SMGEdgePointsTo newEdge = destSMG.getPointer(mta.getValue());
    Assert.assertSame(destObj, newEdge.getObject());
    Assert.assertEquals(0, newEdge.getOffset());

    Assert.assertSame(mta.getValue(), mta.getMapping1().get(value1));
    Assert.assertSame(mta.getValue(), mta.getMapping2().get(value2));
  }
}