/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.geode.cache.client.internal;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Test;

import org.apache.geode.Statistics;
import org.apache.geode.StatisticsFactory;
import org.apache.geode.internal.cache.PoolStats;

public class ConnectionStatsTest {
  private final Statistics stats = mock(Statistics.class);
  private final Statistics sendStats = mock(Statistics.class);
  private final StatisticsFactory statisticsFactory = createStatisticsFactory(sendStats);
  private final PoolStats poolStats = mock(PoolStats.class);
  private final ConnectionStats connectionStats =
      new ConnectionStats(statisticsFactory, "Client", "name", poolStats);

  private final int addPdxTypeDurationId = ConnectionStats.getType().nameToId("addPdxTypeTime");
  private final int addPdxTypeInProgressId =
      ConnectionStats.getType().nameToId("addPdxTypeInProgress");
  private final int addPdxTypeSendDurationId =
      ConnectionStats.getSendType().nameToId("addPdxTypeSendTime");
  private final int addPdxTypeSendInProgressId =
      ConnectionStats.getSendType().nameToId("addPdxTypeSendsInProgress");

  private final int clearDurationId = ConnectionStats.getType().nameToId("clearTime");
  private final int clearInProgressId = ConnectionStats.getType().nameToId("clearsInProgress");
  private final int clearSendDurationId = ConnectionStats.getSendType().nameToId("clearSendTime");
  private final int clearSendInProgressId =
      ConnectionStats.getSendType().nameToId("clearSendsInProgress");

  private final int closeConDurationId = ConnectionStats.getType().nameToId("closeConTime");
  private final int closeConInProgressId =
      ConnectionStats.getType().nameToId("closeConsInProgress");
  private final int closeConSendDurationId =
      ConnectionStats.getSendType().nameToId("closeConSendTime");
  private final int closeConSendInProgressId =
      ConnectionStats.getSendType().nameToId("closeConSendsInProgress");

  private final int closeCQDurationId = ConnectionStats.getType().nameToId("closeCQTime");
  private final int closeCQInProgressId =
      ConnectionStats.getType().nameToId("closeCQsInProgress");
  private final int closeCQSendDurationId =
      ConnectionStats.getSendType().nameToId("closeCQSendTime");
  private final int closeCQSendInProgressId =
      ConnectionStats.getSendType().nameToId("closeCQSendsInProgress");

  private final int createCQDurationId = ConnectionStats.getType().nameToId("createCQTime");
  private final int createCQInProgressId =
      ConnectionStats.getType().nameToId("createCQsInProgress");
  private final int createCQSendDurationId =
      ConnectionStats.getSendType().nameToId("createCQSendTime");
  private final int createCQSendInProgressId =
      ConnectionStats.getSendType().nameToId("createCQSendsInProgress");

  private final int commitDurationId = ConnectionStats.getType().nameToId("commitTime");
  private final int commitInProgressId =
      ConnectionStats.getType().nameToId("commitsInProgress");
  private final int commitSendDurationId =
      ConnectionStats.getSendType().nameToId("commitSendTime");
  private final int commitSendInProgressId =
      ConnectionStats.getSendType().nameToId("commitSendsInProgress");

  private final int containsKeyDurationId = ConnectionStats.getType().nameToId("containsKeyTime");
  private final int containsKeyInProgressId =
      ConnectionStats.getType().nameToId("containsKeysInProgress");
  private final int containsKeySendDurationId =
      ConnectionStats.getSendType().nameToId("containsKeySendTime");
  private final int containsKeySendInProgressId =
      ConnectionStats.getSendType().nameToId("containsKeySendsInProgress");

  private final int destroyRegionDurationId =
      ConnectionStats.getType().nameToId("destroyRegionTime");
  private final int destroyRegionInProgressId =
      ConnectionStats.getType().nameToId("destroyRegionsInProgress");
  private final int destroyRegionSendDurationId =
      ConnectionStats.getSendType().nameToId("destroyRegionSendTime");
  private final int destroyRegionSendInProgressId =
      ConnectionStats.getSendType().nameToId("destroyRegionSendsInProgress");

  private final int destroyDurationId = ConnectionStats.getType().nameToId("destroyTime");
  private final int destroyInProgressId = ConnectionStats.getType().nameToId("destroysInProgress");
  private final int destroySendDurationId =
      ConnectionStats.getSendType().nameToId("destroySendTime");
  private final int destroySendInProgressId =
      ConnectionStats.getSendType().nameToId("destroySendsInProgress");

  private final int executeFunctionDurationId =
      ConnectionStats.getType().nameToId("executeFunctionTime");
  private final int executeFunctionInProgressId =
      ConnectionStats.getType().nameToId("executeFunctionsInProgress");
  private final int executeFunctionSendDurationId =
      ConnectionStats.getSendType().nameToId("executeFunctionSendTime");
  private final int executeFunctionSendInProgressId =
      ConnectionStats.getSendType().nameToId("executeFunctionSendsInProgress");

  private final int gatewayBatchDurationId = ConnectionStats.getType().nameToId("gatewayBatchTime");
  private final int gatewayBatchInProgressId =
      ConnectionStats.getType().nameToId("gatewayBatchsInProgress");
  private final int gatewayBatchSendDurationId =
      ConnectionStats.getSendType().nameToId("gatewayBatchSendTime");
  private final int gatewayBatchSendInProgressId =
      ConnectionStats.getSendType().nameToId("gatewayBatchSendsInProgress");

  private final int getAllDurationId = ConnectionStats.getType().nameToId("getAllTime");
  private final int getAllInProgressId = ConnectionStats.getType().nameToId("getAllsInProgress");
  private final int getAllSendDurationId = ConnectionStats.getSendType().nameToId("getAllSendTime");
  private final int getAllSendInProgressId =
      ConnectionStats.getSendType().nameToId("getAllSendsInProgress");

  private final int getClientPartitionAttributesDurationId =
      ConnectionStats.getType().nameToId("getClientPartitionAttributesTime");
  private final int getClientPartitionAttributesInProgressId =
      ConnectionStats.getType().nameToId("getClientPartitionAttributesInProgress");
  private final int getClientPartitionAttributesSendDurationId =
      ConnectionStats.getSendType().nameToId("getClientPartitionAttributesSendTime");
  private final int getClientPartitionAttributesSendInProgressId =
      ConnectionStats.getSendType().nameToId("getClientPartitionAttributesSendsInProgress");

  private final int getClientPRMetadataDurationId =
      ConnectionStats.getType().nameToId("getClientPRMetadataTime");
  private final int getClientPRMetadataInProgressId =
      ConnectionStats.getType().nameToId("getClientPRMetadataInProgress");
  private final int getClientPRMetadataSendDurationId =
      ConnectionStats.getSendType().nameToId("getClientPRMetadataSendTime");
  private final int getClientPRMetadataSendInProgressId =
      ConnectionStats.getSendType().nameToId("getClientPRMetadataSendsInProgress");

  private final int getPDXIdForTypeDurationId =
      ConnectionStats.getType().nameToId("getPDXIdForTypeTime");
  private final int getPDXIdForTypeInProgressId =
      ConnectionStats.getType().nameToId("getPDXIdForTypeInProgress");
  private final int getPDXIdForTypeSendDurationId =
      ConnectionStats.getSendType().nameToId("getPDXIdForTypeSendTime");
  private final int getPDXIdForTypeSendInProgressId =
      ConnectionStats.getSendType().nameToId("getPDXIdForTypeSendsInProgress");

  private final int getPDXTypeByIdDurationId =
      ConnectionStats.getType().nameToId("getPDXTypeByIdTime");
  private final int getPDXTypeByIdInProgressId =
      ConnectionStats.getType().nameToId("getPDXTypeByIdInProgress");
  private final int getPDXTypeByIdSendDurationId =
      ConnectionStats.getSendType().nameToId("getPDXTypeByIdSendTime");
  private final int getPDXTypeByIdSendInProgressId =
      ConnectionStats.getSendType().nameToId("getPDXTypeByIdSendsInProgress");

  private final int getEntryDurationId = ConnectionStats.getType().nameToId("getEntryTime");
  private final int getEntryInProgressId =
      ConnectionStats.getType().nameToId("getEntrysInProgress");
  private final int getEntrySendDurationId =
      ConnectionStats.getSendType().nameToId("getEntrySendTime");
  private final int getEntrySendInProgressId =
      ConnectionStats.getSendType().nameToId("getEntrySendsInProgress");

  private final int getDurableCQsDurationId =
      ConnectionStats.getType().nameToId("getDurableCQsTime");
  private final int getDurableCQsInProgressId =
      ConnectionStats.getType().nameToId("getDurableCQsInProgress");
  private final int getDurableCQsSendDurationId =
      ConnectionStats.getSendType().nameToId("getDurableCQsSendTime");
  private final int getDurableCQsSendInProgressId =
      ConnectionStats.getSendType().nameToId("getDurableCQsSendsInProgress");

  private final int getDurationId = ConnectionStats.getType().nameToId("getTime");
  private final int getInProgressId = ConnectionStats.getType().nameToId("getsInProgress");
  private final int getSendDurationId = ConnectionStats.getSendType().nameToId("getSendTime");
  private final int getSendInProgressId =
      ConnectionStats.getSendType().nameToId("getSendsInProgress");

  private final int invalidateDurationId = ConnectionStats.getType().nameToId("invalidateTime");
  private final int invalidateInProgressId =
      ConnectionStats.getType().nameToId("invalidatesInProgress");
  private final int invalidateSendDurationId =
      ConnectionStats.getSendType().nameToId("invalidateSendTime");
  private final int invalidateSendInProgressId =
      ConnectionStats.getSendType().nameToId("invalidateSendsInProgress");

  private final int jtaSynchronizationDurationId =
      ConnectionStats.getType().nameToId("jtaSynchronizationTime");
  private final int jtaSynchronizationInProgressId =
      ConnectionStats.getType().nameToId("jtaSynchronizationsInProgress");
  private final int jtaSynchronizationSendDurationId =
      ConnectionStats.getSendType().nameToId("jtaSynchronizationSendTime");
  private final int jtaSynchronizationSendInProgressId =
      ConnectionStats.getSendType().nameToId("jtaSynchronizationSendsInProgress");

  private final int keySetDurationId = ConnectionStats.getType().nameToId("keySetTime");
  private final int keySetInProgressId = ConnectionStats.getType().nameToId("keySetsInProgress");
  private final int keySetSendDurationId = ConnectionStats.getSendType().nameToId("keySetSendTime");
  private final int keySetSendInProgressId =
      ConnectionStats.getSendType().nameToId("keySetSendsInProgress");

  private final int makePrimaryDurationId = ConnectionStats.getType().nameToId("makePrimaryTime");
  private final int makePrimaryInProgressId =
      ConnectionStats.getType().nameToId("makePrimarysInProgress");
  private final int makePrimarySendDurationId =
      ConnectionStats.getSendType().nameToId("makePrimarySendTime");
  private final int makePrimarySendInProgressId =
      ConnectionStats.getSendType().nameToId("makePrimarySendsInProgress");

  private final int pingDurationId = ConnectionStats.getType().nameToId("pingTime");
  private final int pingInProgressId = ConnectionStats.getType().nameToId("pingsInProgress");
  private final int pingSendDurationId = ConnectionStats.getSendType().nameToId("pingSendTime");
  private final int pingSendInProgressId =
      ConnectionStats.getSendType().nameToId("pingSendsInProgress");

  private final int primaryAckDurationId = ConnectionStats.getType().nameToId("primaryAckTime");
  private final int primaryAckInProgressId =
      ConnectionStats.getType().nameToId("primaryAcksInProgress");
  private final int primaryAckSendDurationId =
      ConnectionStats.getSendType().nameToId("primaryAckSendTime");
  private final int primaryAckSendInProgressId =
      ConnectionStats.getSendType().nameToId("primaryAckSendsInProgress");

  private final int putAllDurationId = ConnectionStats.getType().nameToId("putAllTime");
  private final int putAllInProgressId = ConnectionStats.getType().nameToId("putAllsInProgress");
  private final int putAllSendDurationId = ConnectionStats.getSendType().nameToId("putAllSendTime");
  private final int putAllSendInProgressId =
      ConnectionStats.getSendType().nameToId("putAllSendsInProgress");

  private final int putDurationId = ConnectionStats.getType().nameToId("putTime");
  private final int putInProgressId = ConnectionStats.getType().nameToId("putsInProgress");
  private final int putSendDurationId = ConnectionStats.getSendType().nameToId("putSendTime");
  private final int putSendInProgressId =
      ConnectionStats.getSendType().nameToId("putSendsInProgress");

  private final int queryDurationId = ConnectionStats.getType().nameToId("queryTime");
  private final int queryInProgressId = ConnectionStats.getType().nameToId("querysInProgress");
  private final int querySendDurationId = ConnectionStats.getSendType().nameToId("querySendTime");
  private final int querySendInProgressId =
      ConnectionStats.getSendType().nameToId("querySendsInProgress");

  private final int readyForEventsDurationId =
      ConnectionStats.getType().nameToId("readyForEventsTime");
  private final int readyForEventsInProgressId =
      ConnectionStats.getType().nameToId("readyForEventsInProgress");
  private final int readyForEventsSendDurationId =
      ConnectionStats.getSendType().nameToId("readyForEventsSendTime");
  private final int readyForEventsSendInProgressId =
      ConnectionStats.getSendType().nameToId("readyForEventsSendsInProgress");

  private final int registerDataSerializersDurationId =
      ConnectionStats.getType().nameToId("registerDataSerializersTime");
  private final int registerDataSerializersInProgressId =
      ConnectionStats.getType().nameToId("registerDataSerializersInProgress");
  private final int registerDataSerializersSendDurationId =
      ConnectionStats.getSendType().nameToId("registerDataSerializersSendTime");
  private final int registerDataSerializersSendInProgressId =
      ConnectionStats.getSendType().nameToId("registerDataSerializersSendInProgress");

  private final int registerInstantiatorsDurationId =
      ConnectionStats.getType().nameToId("registerInstantiatorsTime");
  private final int registerInstantiatorsInProgressId =
      ConnectionStats.getType().nameToId("registerInstantiatorsInProgress");
  private final int registerInstantiatorsSendDurationId =
      ConnectionStats.getSendType().nameToId("registerInstantiatorsSendTime");
  private final int registerInstantiatorsSendInProgressId =
      ConnectionStats.getSendType().nameToId("registerInstantiatorsSendsInProgress");

  private final int registerInterestDurationId =
      ConnectionStats.getType().nameToId("registerInterestTime");
  private final int registerInterestInProgressId =
      ConnectionStats.getType().nameToId("registerInterestsInProgress");
  private final int registerInterestSendDurationId =
      ConnectionStats.getSendType().nameToId("registerInterestSendTime");
  private final int registerInterestSendInProgressId =
      ConnectionStats.getSendType().nameToId("registerInterestSendsInProgress");

  private final int removeAllDurationId = ConnectionStats.getType().nameToId("removeAllTime");
  private final int removeAllInProgressId =
      ConnectionStats.getType().nameToId("removeAllsInProgress");
  private final int removeAllSendDurationId =
      ConnectionStats.getSendType().nameToId("removeAllSendTime");
  private final int removeAllSendInProgressId =
      ConnectionStats.getSendType().nameToId("removeAllSendsInProgress");

  private final int rollbackDurationId = ConnectionStats.getType().nameToId("rollbackTime");
  private final int rollbackInProgressId =
      ConnectionStats.getType().nameToId("rollbacksInProgress");
  private final int rollbackSendDurationId =
      ConnectionStats.getSendType().nameToId("rollbackSendTime");
  private final int rollbackSendInProgressId =
      ConnectionStats.getSendType().nameToId("rollbackSendsInProgress");

  private final int sizeDurationId = ConnectionStats.getType().nameToId("sizeTime");
  private final int sizeInProgressId = ConnectionStats.getType().nameToId("sizesInProgress");
  private final int sizeSendDurationId = ConnectionStats.getSendType().nameToId("sizeSendTime");
  private final int sizeSendInProgressId =
      ConnectionStats.getSendType().nameToId("sizeSendsInProgress");

  private final int stopCQDurationId = ConnectionStats.getType().nameToId("stopCQTime");
  private final int stopCQInProgressId = ConnectionStats.getType().nameToId("stopCQsInProgress");
  private final int stopCQSendDurationId = ConnectionStats.getSendType().nameToId("stopCQSendTime");
  private final int stopCQSendInProgressId =
      ConnectionStats.getSendType().nameToId("stopCQSendsInProgress");

  private final int txFailoverDurationId = ConnectionStats.getType().nameToId("txFailoverTime");
  private final int txFailoverInProgressId =
      ConnectionStats.getType().nameToId("txFailoversInProgress");
  private final int txFailoverSendDurationId =
      ConnectionStats.getSendType().nameToId("txFailoverSendTime");
  private final int txFailoverSendInProgressId =
      ConnectionStats.getSendType().nameToId("txFailoverSendsInProgress");

  private final int unregisterInterestDurationId =
      ConnectionStats.getType().nameToId("unregisterInterestTime");
  private final int unregisterInterestInProgressId =
      ConnectionStats.getType().nameToId("unregisterInterestsInProgress");
  private final int unregisterInterestSendDurationId =
      ConnectionStats.getSendType().nameToId("unregisterInterestSendTime");
  private final int unregisterInterestSendInProgressId =
      ConnectionStats.getSendType().nameToId("unregisterInterestSendsInProgress");

  private StatisticsFactory createStatisticsFactory(Statistics sendStats) {
    StatisticsFactory statisticsFactory = mock(StatisticsFactory.class);
    when(statisticsFactory.createAtomicStatistics(any(), eq("ClientStats-name")))
        .thenReturn(stats);
    when(statisticsFactory.createAtomicStatistics(any(), eq("ClientSendStats-name")))
        .thenReturn(sendStats);
    return statisticsFactory;
  }

  @Test
  public void close() {
    connectionStats.close();
    verify(stats).close();
    verify(sendStats).close();
  }

  @Test
  public void incMessagesBeingReceived() {
    int messagesStatId = ConnectionStats.getType().nameToId("messagesBeingReceived");
    int messagesBytesStatId = ConnectionStats.getType().nameToId("messageBytesBeingReceived");
    connectionStats.incMessagesBeingReceived(10);
    verify(stats).incInt(messagesStatId, 1);
    verify(stats).incLong(messagesBytesStatId, 10);
  }

  @Test
  public void incMessagesBeingReceived_messageSizeIsZero() {
    int messagesStatId = ConnectionStats.getType().nameToId("messagesBeingReceived");
    int messagesBytesStatId = ConnectionStats.getType().nameToId("messageBytesBeingReceived");
    connectionStats.incMessagesBeingReceived(0);
    verify(stats).incInt(messagesStatId, 1);
    verify(stats, never()).incLong(eq(messagesBytesStatId), anyInt());
  }

  @Test
  public void decMessagesBeingReceived() {
    int messagesStatId = ConnectionStats.getType().nameToId("messagesBeingReceived");
    int messagesBytesStatId = ConnectionStats.getType().nameToId("messageBytesBeingReceived");
    connectionStats.decMessagesBeingReceived(10);
    verify(stats).incInt(messagesStatId, -1);
    verify(stats).incLong(messagesBytesStatId, -10);
  }

  @Test
  public void decMessagesBeingReceived_messageSizeIsZero() {
    int messagesStatId = ConnectionStats.getType().nameToId("messagesBeingReceived");
    int messagesBytesStatId = ConnectionStats.getType().nameToId("messageBytesBeingReceived");
    connectionStats.decMessagesBeingReceived(0);
    verify(stats).incInt(messagesStatId, -1);
    verify(stats, never()).incLong(eq(messagesBytesStatId), anyInt());
  }

  @Test
  public void endAddPdxType_TimeoutOperation() {
    int statId = ConnectionStats.getType().nameToId("addPdxTypeTimeouts");

    connectionStats.endAddPdxType(1, true, true);

    verify(stats).incInt(eq(addPdxTypeInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(addPdxTypeDurationId), anyLong());
  }

  @Test
  public void endAddPdxType_TimeoutOperationAndNotFailed() {
    int statId = ConnectionStats.getType().nameToId("addPdxTypeTimeouts");

    connectionStats.endAddPdxType(1, true, false);

    verify(stats).incInt(eq(addPdxTypeInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(addPdxTypeDurationId), anyLong());
  }

  @Test
  public void endAddPdxType_FailedOperation() {
    int statId = ConnectionStats.getType().nameToId("addPdxTypeFailures");

    connectionStats.endAddPdxType(1, false, true);

    verify(stats).incInt(eq(addPdxTypeInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(addPdxTypeDurationId), anyLong());
  }

  @Test
  public void endAddPdxType_SuccessfulOperation() {
    int statId = ConnectionStats.getType().nameToId("addPdxTypeSuccessful");

    connectionStats.endAddPdxType(1, false, false);

    verify(stats).incInt(eq(addPdxTypeInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(addPdxTypeDurationId), anyLong());
  }

  @Test
  public void endAddPdxTypeSend_FailedOperation() {
    int statId = ConnectionStats.getSendType().nameToId("addPdxTypeSendFailures");

    connectionStats.endAddPdxTypeSend(1, true);

    verify(sendStats).incInt(eq(addPdxTypeSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(addPdxTypeSendDurationId), anyLong());
  }

  @Test
  public void endAddPdxTypeSend_SuccessfulOperation() {
    int statId = ConnectionStats.getSendType().nameToId("addPdxTypeSendsSuccessful");

    connectionStats.endAddPdxTypeSend(1, false);

    verify(sendStats).incInt(eq(addPdxTypeSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(addPdxTypeSendDurationId), anyLong());
  }

  @Test
  public void startAddPdx() {
    int statId = ConnectionStats.getType().nameToId("addPdxTypeInProgress");
    int sendStatId = ConnectionStats.getSendType().nameToId("addPdxTypeSendsInProgress");

    connectionStats.startAddPdxType();

    verify(stats).incInt(statId, 1);
    verify(sendStats).incInt(sendStatId, 1);
  }

  @Test
  public void endClearSend_FailedOperation() {
    int statId = ConnectionStats.getSendType().nameToId("clearSendFailures");

    connectionStats.endClearSend(1, true);

    verify(sendStats).incInt(eq(clearSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(clearSendDurationId), anyLong());
  }

  @Test
  public void endClearSend_SuccessfulOperation() {
    int statId = ConnectionStats.getSendType().nameToId("clearSends");

    connectionStats.endClearSend(1, false);

    verify(sendStats).incInt(eq(clearSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(clearSendDurationId), anyLong());
  }

  @Test
  public void endClear_TimeoutOperation() {
    int statId = ConnectionStats.getType().nameToId("clearTimeouts");

    connectionStats.endClear(1, true, true);

    verify(stats).incInt(eq(clearInProgressId), eq(-1));
    verify(stats).incLong(statId, 1L);
    verify(stats).incLong(eq(clearDurationId), anyLong());
  }

  @Test
  public void endClear_TimeoutOperationAndNotFailed() {
    int statId = ConnectionStats.getType().nameToId("clearTimeouts");

    connectionStats.endClear(1, true, false);

    verify(stats).incInt(eq(clearInProgressId), eq(-1));
    verify(stats).incLong(statId, 1L);
    verify(stats).incLong(eq(clearDurationId), anyLong());
  }

  @Test
  public void endClear_FailedOperation() {
    int statId = ConnectionStats.getType().nameToId("clearFailures");

    connectionStats.endClear(1, false, true);

    verify(stats).incInt(eq(clearInProgressId), eq(-1));
    verify(stats).incLong(statId, 1L);
    verify(stats).incLong(eq(clearDurationId), anyLong());
  }

  @Test
  public void endClear_SuccessfulOperation() {
    int statId = ConnectionStats.getType().nameToId("clears");

    connectionStats.endClear(1, false, false);

    verify(stats).incInt(eq(clearInProgressId), eq(-1));
    verify(stats).incLong(statId, 1L);
    verify(stats).incLong(eq(clearDurationId), anyLong());
  }

  @Test
  public void startClear() {
    int statId = ConnectionStats.getType().nameToId("clearsInProgress");
    int sendStatId = ConnectionStats.getSendType().nameToId("clearSendsInProgress");

    connectionStats.startClear();

    verify(stats).incInt(statId, 1);
    verify(sendStats).incInt(sendStatId, 1);
  }

  @Test
  public void endCloseConSend_FailedOperation() {
    int statId = ConnectionStats.getSendType().nameToId("closeConSendFailures");

    connectionStats.endCloseConSend(1, true);

    verify(sendStats).incInt(eq(closeConSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(closeConSendDurationId), anyLong());
  }

  @Test
  public void endCloseConSend_SuccessfulOperation() {
    int statId = ConnectionStats.getSendType().nameToId("closeConSends");

    connectionStats.endCloseConSend(1, false);

    verify(sendStats).incInt(eq(closeConSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(closeConSendDurationId), anyLong());
  }

  @Test
  public void endCloseCon_TimeoutOperation() {
    int statId = ConnectionStats.getType().nameToId("closeConTimeouts");

    connectionStats.endCloseCon(1, true, true);

    verify(stats).incInt(eq(closeConInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(closeConDurationId), anyLong());
  }

  @Test
  public void endCloseCon_TimeoutOperationAndNotFailed() {
    int statId = ConnectionStats.getType().nameToId("closeConTimeouts");

    connectionStats.endCloseCon(1, true, false);

    verify(stats).incInt(eq(closeConInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(closeConDurationId), anyLong());
  }

  @Test
  public void endCloseCon_FailedOperation() {
    int statId = ConnectionStats.getType().nameToId("closeConFailures");

    connectionStats.endCloseCon(1, false, true);

    verify(stats).incInt(eq(closeConInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(closeConDurationId), anyLong());
  }

  @Test
  public void endCloseCon_SuccessfulOperation() {
    int statId = ConnectionStats.getType().nameToId("closeCons");

    connectionStats.endCloseCon(1, false, false);

    verify(stats).incInt(eq(closeConInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(closeConDurationId), anyLong());
  }

  @Test
  public void startCloseCon() {
    int statId = ConnectionStats.getType().nameToId("closeConsInProgress");
    int sendStatId = ConnectionStats.getSendType().nameToId("closeConSendsInProgress");

    connectionStats.startCloseCon();

    verify(stats).incInt(statId, 1);
    verify(sendStats).incInt(sendStatId, 1);
  }

  @Test
  public void endCloseCQSend_FailedOperation() {
    int statId = ConnectionStats.getSendType().nameToId("closeCQSendFailures");

    connectionStats.endCloseCQSend(1, true);

    verify(sendStats).incInt(eq(closeCQSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(closeCQSendDurationId), anyLong());
  }

  @Test
  public void endCloseCQSend_SuccessfulOperation() {
    int statId = ConnectionStats.getSendType().nameToId("closeCQSends");

    connectionStats.endCloseCQSend(1, false);

    verify(sendStats).incInt(eq(closeCQSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(closeCQSendDurationId), anyLong());
  }

  @Test
  public void endCloseCQ_TimeoutOperation() {
    int statId = ConnectionStats.getType().nameToId("closeCQTimeouts");

    connectionStats.endCloseCQ(1, true, true);

    verify(stats).incInt(eq(closeCQInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(closeCQDurationId), anyLong());
  }

  @Test
  public void endCloseCQ_TimeoutOperationAndNotFailed() {
    int statId = ConnectionStats.getType().nameToId("closeCQTimeouts");

    connectionStats.endCloseCQ(1, true, false);

    verify(stats).incInt(eq(closeCQInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(closeCQDurationId), anyLong());
  }

  @Test
  public void endCloseCQ_FailedOperation() {
    int statId = ConnectionStats.getType().nameToId("closeCQFailures");

    connectionStats.endCloseCQ(1, false, true);

    verify(stats).incInt(eq(closeCQInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(closeCQDurationId), anyLong());
  }

  @Test
  public void endCloseCQ_SuccessfulOperation() {
    int statId = ConnectionStats.getType().nameToId("closeCQs");

    connectionStats.endCloseCQ(1, false, false);

    verify(stats).incInt(eq(closeCQInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(closeCQDurationId), anyLong());
  }

  @Test
  public void startCloseCQ() {
    int statId = ConnectionStats.getType().nameToId("closeCQsInProgress");
    int sendStatId = ConnectionStats.getSendType().nameToId("closeCQSendsInProgress");

    connectionStats.startCloseCQ();

    verify(stats).incInt(statId, 1);
    verify(sendStats).incInt(sendStatId, 1);
  }

  @Test
  public void endCreateCQSend_FailedOperation() {
    int statId = ConnectionStats.getSendType().nameToId("createCQSendFailures");

    connectionStats.endCreateCQSend(1, true);

    verify(sendStats).incInt(eq(createCQSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(createCQSendDurationId), anyLong());
  }

  @Test
  public void endCreateCQSend_SuccessfulOperation() {
    int statId = ConnectionStats.getSendType().nameToId("createCQSends");

    connectionStats.endCreateCQSend(1, false);

    verify(sendStats).incInt(eq(createCQSendInProgressId), eq(-1));
    verify(sendStats).incInt(statId, 1);
    verify(sendStats).incLong(eq(createCQSendDurationId), anyLong());
  }

  @Test
  public void endCreateCQ_TimeoutOperation() {
    int statId = ConnectionStats.getType().nameToId("createCQTimeouts");

    connectionStats.endCreateCQ(1, true, true);

    verify(stats).incInt(eq(createCQInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(createCQDurationId), anyLong());
  }

  @Test
  public void endCreateCQ_TimeoutOperationAndNotFailed() {
    int statId = ConnectionStats.getType().nameToId("createCQTimeouts");

    connectionStats.endCreateCQ(1, true, false);

    verify(stats).incInt(eq(createCQInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(createCQDurationId), anyLong());
  }

  @Test
  public void endCreateCQ_FailedOperation() {
    int statId = ConnectionStats.getType().nameToId("createCQFailures");

    connectionStats.endCreateCQ(1, false, true);

    verify(stats).incInt(eq(createCQInProgressId), eq(-1));
    verify(stats).incInt(statId, 1);
    verify(stats).incLong(eq(createCQDurationId), anyLong());
  }

  @Test
