@VisibleForTesting
  void handleResponse(DiscoveryResponseData response)
  {
    ResourceType resourceType = response.getResourceType();
    switch (resourceType)
    {
      case NODE:
        handleD2NodeResponse(response);
        break;
      case D2_URI_MAP:
        handleD2URIMapResponse(response);
        break;
      case D2_URI:
        handleD2URICollectionResponse(response);
        break;
      default:
        throw new AssertionError("Missing case in enum switch: " + resourceType);
    }
  }
@Test
  public void testHandleD2URICollectionResponseWithData()
  {
    DiscoveryResponseData createUri1 = new DiscoveryResponseData(D2_URI, Collections.singletonList(
        Resource.newBuilder()
            .setVersion(VERSION1)
            .setName(URI_URN1)
            .setResource(Any.pack(D2URI_1))
            .build()
    ), null, NONCE, null);
    XdsClientImplFixture fixture = new XdsClientImplFixture();
    // subscriber original data is null
    fixture._clusterSubscriber.setData(null);
    fixture._xdsClientImpl.handleResponse(createUri1);
    fixture.verifyAckSent(1);
    verify(fixture._resourceWatcher).onChanged(eq(D2_URI_MAP_UPDATE_WITH_DATA1));
    verifyZeroInteractions(fixture._serverMetricsProvider);
    D2URIMapUpdate actualData = (D2URIMapUpdate) fixture._clusterSubscriber.getData();
    // subscriber data should be updated to D2_URI_MAP_UPDATE_WITH_DATA1
    Assert.assertEquals(Objects.requireNonNull(actualData).getURIMap(), D2_URI_MAP_UPDATE_WITH_DATA1.getURIMap());

    // subscriber original data is invalid, xds server latency won't be tracked
    fixture._clusterSubscriber.setData(new D2URIMapUpdate(null));
    fixture._xdsClientImpl.handleResponse(createUri1);
    fixture.verifyAckSent(2);
    verify(fixture._resourceWatcher, times(2)).onChanged(eq(D2_URI_MAP_UPDATE_WITH_DATA1));
    verifyZeroInteractions(fixture._serverMetricsProvider);

    DiscoveryResponseData createUri2Delete1 = new DiscoveryResponseData(D2_URI, Collections.singletonList(
        Resource.newBuilder()
            .setVersion(VERSION1)
            .setName(URI_URN2)
            .setResource(Any.pack(D2URI_2))
            .build()
    ), Collections.singletonList(URI_URN1), NONCE, null);
    fixture._xdsClientImpl.handleResponse(createUri2Delete1);
    actualData = (D2URIMapUpdate) fixture._clusterSubscriber.getData();
    // subscriber data should be updated to D2_URI_MAP_UPDATE_WITH_DATA2
    D2URIMapUpdate expectedUpdate = new D2URIMapUpdate(Collections.singletonMap(URI2, D2URI_2));
    verify(fixture._resourceWatcher).onChanged(eq(expectedUpdate));
    // track latency only for updated/new uri (not for deletion)
    verify(fixture._serverMetricsProvider).trackLatency(anyLong());
    Assert.assertEquals(actualData.getURIMap(), expectedUpdate.getURIMap());
    fixture.verifyAckSent(3);

    // Finally sanity check that the client correctly handles the deletion of the final URI in the collection
    DiscoveryResponseData deleteUri2 =
        new DiscoveryResponseData(D2_URI, null, Collections.singletonList(URI_URN2), NONCE, null);
    fixture._xdsClientImpl.handleResponse(deleteUri2);
    actualData = (D2URIMapUpdate) fixture._clusterSubscriber.getData();
    // subscriber data should be updated to empty map
    expectedUpdate = new D2URIMapUpdate(Collections.emptyMap());
    verify(fixture._resourceWatcher).onChanged(eq(expectedUpdate));
    verifyNoMoreInteractions(fixture._serverMetricsProvider);
    Assert.assertEquals(actualData.getURIMap(), expectedUpdate.getURIMap());
    fixture.verifyAckSent(4);
  }