// Test Group creation before AUDIT process
 private void testGroupCreationBeforeAudit(DeviceId deviceId) {
   PortNumber[] ports1 = {PortNumber.portNumber(31), PortNumber.portNumber(32)};
   PortNumber[] ports2 = {PortNumber.portNumber(41), PortNumber.portNumber(42)};
   GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
   List<GroupBucket> buckets = new ArrayList<>();
   List<PortNumber> outPorts = new ArrayList<>();
   outPorts.addAll(Arrays.asList(ports1));
   outPorts.addAll(Arrays.asList(ports2));
   for (PortNumber portNumber : outPorts) {
     TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
     tBuilder
         .setOutput(portNumber)
         .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
         .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
         .pushMpls()
         .setMpls(MplsLabel.mplsLabel(106));
     buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
   }
   GroupBuckets groupBuckets = new GroupBuckets(buckets);
   GroupDescription newGroupDesc =
       new DefaultGroupDescription(deviceId, Group.Type.SELECT, groupBuckets, key, null, appId);
   groupService.addGroup(newGroupDesc);
   assertEquals(null, groupService.getGroup(deviceId, key));
   assertEquals(0, Iterables.size(groupService.getGroups(deviceId, appId)));
 }
  // Test group add bucket operations
  private void testAddBuckets(DeviceId deviceId) {
    GroupKey addKey = new DefaultGroupKey("group1AddBuckets".getBytes());

    GroupKey prevKey = new DefaultGroupKey("group1BeforeAudit".getBytes());
    Group createdGroup = groupService.getGroup(deviceId, prevKey);
    List<GroupBucket> buckets = new ArrayList<>();
    buckets.addAll(createdGroup.buckets().buckets());

    PortNumber[] addPorts = {PortNumber.portNumber(51), PortNumber.portNumber(52)};
    List<PortNumber> outPorts;
    outPorts = new ArrayList<>();
    outPorts.addAll(Arrays.asList(addPorts));
    List<GroupBucket> addBuckets;
    addBuckets = new ArrayList<>();
    for (PortNumber portNumber : outPorts) {
      TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
      tBuilder
          .setOutput(portNumber)
          .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
          .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
          .pushMpls()
          .setMpls(MplsLabel.mplsLabel(106));
      addBuckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
      buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
    }
    GroupBuckets groupAddBuckets = new GroupBuckets(addBuckets);
    groupService.addBucketsToGroup(deviceId, prevKey, groupAddBuckets, addKey, appId);
    GroupBuckets updatedBuckets = new GroupBuckets(buckets);
    List<GroupOperation> expectedGroupOps =
        Collections.singletonList(
            GroupOperation.createModifyGroupOperation(
                createdGroup.id(), Group.Type.SELECT, updatedBuckets));
    if (deviceId.equals(DID)) {
      internalProvider.validate(deviceId, expectedGroupOps);
    } else {
      this.validate(deviceId, expectedGroupOps);
    }
    Group existingGroup = groupService.getGroup(deviceId, addKey);
    List<Group> groupEntries = Collections.singletonList(existingGroup);
    providerService.pushGroupMetrics(deviceId, groupEntries);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATED));
  }
  @Override
  public void next(NextObjective nextObjective) {
    if (nextObjective.type() != NextObjective.Type.BROADCAST) {
      log.error("OLT only supports broadcast groups.");
      fail(nextObjective, ObjectiveError.BADPARAMS);
    }

    if (nextObjective.next().size() != 1) {
      log.error("OLT only supports singleton broadcast groups.");
      fail(nextObjective, ObjectiveError.BADPARAMS);
    }

    TrafficTreatment treatment = nextObjective.next().stream().findFirst().get();

    GroupBucket bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
    GroupKey key = new DefaultGroupKey(appKryo.serialize(nextObjective.id()));

    GroupDescription groupDesc =
        new DefaultGroupDescription(
            deviceId,
            GroupDescription.Type.ALL,
            new GroupBuckets(Collections.singletonList(bucket)),
            key,
            null,
            nextObjective.appId());

    pendingGroups.put(key, nextObjective);

    switch (nextObjective.op()) {
      case ADD:
        groupService.addGroup(groupDesc);
        break;
      case REMOVE:
        groupService.removeGroup(deviceId, key, nextObjective.appId());
        break;
      case ADD_TO_EXISTING:
      case REMOVE_FROM_EXISTING:
        // TODO: handle addition to group when caller signals it.
        break;
      default:
        log.warn("Unknown next objective operation: {}", nextObjective.op());
    }
  }
  private Group createSouthboundGroupEntry(
      GroupId gId, List<PortNumber> ports, long referenceCount, DeviceId deviceId) {
    List<PortNumber> outPorts = new ArrayList<>();
    outPorts.addAll(ports);

    List<GroupBucket> buckets = new ArrayList<>();
    for (PortNumber portNumber : outPorts) {
      TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
      tBuilder
          .setOutput(portNumber)
          .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
          .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
          .pushMpls()
          .setMpls(MplsLabel.mplsLabel(106));
      buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
    }
    GroupBuckets groupBuckets = new GroupBuckets(buckets);
    StoredGroupEntry group = new DefaultGroup(gId, deviceId, Group.Type.SELECT, groupBuckets);
    group.setReferenceCount(referenceCount);
    return group;
  }
  private void groupOperationFaliure(DeviceId deviceId) {
    PortNumber[] ports1 = {PortNumber.portNumber(31), PortNumber.portNumber(32)};
    PortNumber[] ports2 = {PortNumber.portNumber(41), PortNumber.portNumber(42)};
    // Test Group creation before AUDIT process
    GroupKey key = new DefaultGroupKey("group1BeforeAudit".getBytes());
    List<GroupBucket> buckets = new ArrayList<>();
    List<PortNumber> outPorts = new ArrayList<>();
    outPorts.addAll(Arrays.asList(ports1));
    outPorts.addAll(Arrays.asList(ports2));
    for (PortNumber portNumber : outPorts) {
      TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
      tBuilder
          .setOutput(portNumber)
          .setEthDst(MacAddress.valueOf("00:00:00:00:00:02"))
          .setEthSrc(MacAddress.valueOf("00:00:00:00:00:01"))
          .pushMpls()
          .setMpls(MplsLabel.mplsLabel(106));
      buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
    }
    GroupBuckets groupBuckets = new GroupBuckets(buckets);
    GroupDescription newGroupDesc =
        new DefaultGroupDescription(deviceId, Group.Type.SELECT, groupBuckets, key, null, appId);
    groupService.addGroup(newGroupDesc);

    // Test initial group audit process
    GroupId gId1 = new DefaultGroupId(1);
    Group group1 = createSouthboundGroupEntry(gId1, Arrays.asList(ports1), 0, deviceId);
    GroupId gId2 = new DefaultGroupId(2);
    // Non zero reference count will make the group manager to queue
    // the extraneous groups until reference count is zero.
    Group group2 = createSouthboundGroupEntry(gId2, Arrays.asList(ports2), 2, deviceId);
    List<Group> groupEntries = Arrays.asList(group1, group2);
    providerService.pushGroupMetrics(deviceId, groupEntries);
    Group createdGroup = groupService.getGroup(deviceId, key);

    // Group Add failure test
    GroupOperation groupAddOp =
        GroupOperation.createAddGroupOperation(
            createdGroup.id(), createdGroup.type(), createdGroup.buckets());
    providerService.groupOperationFailed(deviceId, groupAddOp);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_ADD_FAILED));

    // Group Mod failure test
    groupService.addGroup(newGroupDesc);
    createdGroup = groupService.getGroup(deviceId, key);
    assertNotNull(createdGroup);

    GroupOperation groupModOp =
        GroupOperation.createModifyGroupOperation(
            createdGroup.id(), createdGroup.type(), createdGroup.buckets());
    providerService.groupOperationFailed(deviceId, groupModOp);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_UPDATE_FAILED));

    // Group Delete failure test
    groupService.addGroup(newGroupDesc);
    createdGroup = groupService.getGroup(deviceId, key);
    assertNotNull(createdGroup);

    GroupOperation groupDelOp =
        GroupOperation.createDeleteGroupOperation(createdGroup.id(), createdGroup.type());
    providerService.groupOperationFailed(deviceId, groupDelOp);
    internalListener.validateEvent(Collections.singletonList(GroupEvent.Type.GROUP_REMOVE_FAILED));
  }