import torch


def test(model, testloader, criterion, device):
    """
    Returns (loss, accuracy) of model w.r.t. the given  testloader.
    """
    # Switch to evaluation mode, and CUDA if possible
##    _mem_monitor("TEST 0", device)
    model.eval()
    model.to(device)
##    _mem_monitor("TEST 1 (model loaded)", device)

    losses = 0
    correct = 0
    with torch.no_grad():
        for images, labels in testloader:
            images, labels = images.to(device), labels.to(device)
##            _mem_monitor("TEST 2 (images loaded)", device) # ===== monitoring =====

            # Forward step and loss computation
            output = model(images)    # Is Torch([b, 102])
            losses += criterion(output, labels).item()
            _, predicted = torch.max(output.data, 1)  # (values, indices)
            correct +=  (predicted == labels).sum().item()

    # Switch back to training mode
    model.train()

    testloader_size = len(testloader.dataset)
    accuracy = correct / testloader_size
    loss = losses / len(testloader) # consistent with training loss
##    _mem_monitor("TEST 3 (END)", device)

    return loss, accuracy