@VisibleForTesting
    int getNumAllocatedSlotsOfGroup(long groupId) {
        return allocatedGroupIdToSlotCount.getOrDefault(groupId, 0);
    }
@Test
    public void testReleaseSlot() throws InterruptedException {
        DefaultSlotSelectionStrategy strategy =
                new DefaultSlotSelectionStrategy(() -> false, (groupId) -> false);
        SlotTracker slotTracker = new SlotTracker(ImmutableList.of(strategy));

        LogicalSlot slot1 = generateSlot(1, 0);
        LogicalSlot slot2 = generateSlot(1, 0);

        // 1.1 require slot1.
        assertThat(slotTracker.requireSlot(slot1)).isTrue();
        assertThat(slotTracker.getSlots()).hasSize(1);

        // 1.2 release slot1.
        slotTracker.releaseSlot(slot1.getSlotId());
        assertThat(slotTracker.getNumAllocatedSlots()).isZero();
        assertThat(strategy.getNumAllocatedSlotsOfGroup(0)).isZero();

        // 2.1 require and allocate slot2.
        assertThat(slotTracker.requireSlot(slot2)).isTrue();
        assertThat(slotTracker.getSlots()).hasSize(1);
        slotTracker.allocateSlot(slot2);
        assertThat(slotTracker.getNumAllocatedSlots()).isOne();
        assertThat(strategy.getNumAllocatedSlotsOfGroup(0)).isOne();

        // 2.2 require slot1.
        assertThat(slotTracker.requireSlot(slot1)).isTrue();
        assertThat(slotTracker.getSlots()).hasSize(2);

        // 2.3 release slot1.
        slotTracker.releaseSlot(slot1.getSlotId());
        assertThat(slotTracker.getNumAllocatedSlots()).isOne();
        assertThat(strategy.getNumAllocatedSlotsOfGroup(0)).isOne();

        // 2.4 release slot2
        Thread.sleep(1200); // give enough interval time to trigger sweepEmptyGroups.
        assertThat(slotTracker.releaseSlot(slot2.getSlotId())).isSameAs(slot2);
        assertThat(strategy.getNumAllocatedSlotsOfGroup(0)).isZero();
    }