def test_request_hooks():
    sim = SimulatorKernel(outputDirectory = None)

    loadBalancer = MockLoadBalancer(sim, latency = 1)
    autoScalerController = mock.Mock()
    autoScalerController.controlInterval = 1
    autoScalerController.onRequest = mock.Mock(return_value=0)
    autoScalerController.onCompleted = mock.Mock(return_value=0)
    autoScalerController.onControlPeriod = mock.Mock(return_value=0)
    autoScalerController.onStatus = mock.Mock(return_value=0)

    autoScaler = AutoScaler(sim, loadBalancer, controller = autoScalerController)
    assert str(autoScaler)

    server1 = mock.Mock(name = 'server1')
    server2 = mock.Mock(name = 'server2')
    autoScaler.addBackend(server1)
    autoScaler.addBackend(server2)

    r = Request()
    autoScaler.request(r)
    sim.add(100, lambda: autoScaler.scaleUp())
    sim.run(until = 1000)

    # TODO: Check exact call parameters
    assert autoScalerController.onRequest.call_count == 1, autoScalerController.onRequest.call_count
    assert autoScalerController.onCompleted.call_count == 1, autoScalerController.onCompleted.call_count
    assert autoScalerController.onStatus.call_count == 3, autoScalerController.onStatus.call_count
    assert autoScalerController.onControlPeriod.call_count == 1000, autoScalerController.onControlPeriod.call_count
def test_with_controller_always_yes():
    completedRequests = []

    controller = Mock()
    controller.withOptional.return_value = True, 1

    sim = SimulatorKernel(outputDirectory = None)
    server = Server(sim,
            serviceTimeY = 10, serviceTimeYVariance = 0,
            serviceTimeN =  1, serviceTimeNVariance = 0)
    server.controller = controller

    r = Request()
    r.onCompleted = lambda: completedRequests.append(r)
    sim.add(0, lambda: server.request(r))
    
    r2 = Request()
    r2.onCompleted = lambda: completedRequests.append(r2)
    sim.add(0, lambda: server.request(r2))

    sim.run()

    controller.withOptional.assert_called_with()

    assert set(completedRequests) == set([ r, r2 ])
    assert abs(server.getActiveTime() - 20.0) < eps, server.getActiveTime()
def test_closed_client_off():
    sim = SimulatorKernel()
    server = MockServer(sim)
    client = ClosedLoopClient(sim, server)
    sim.add(10, lambda: client.deactivate())
    sim.run(until=100)

    assert server.numSeenRequests < 20, server.numSeenRequests
def test_open_client_off():
    sim = SimulatorKernel()
    server = MockServer(sim)
    clients = OpenLoopClient(sim, server)
    clients.setRate(10)
    sim.add(10, lambda: clients.setRate(0))
    sim.run(until=100)

    assert server.numSeenRequests < 110, server.numSeenRequests
def test_remove_while_request_not_in_progress():
    sim = SimulatorKernel(outputDirectory = None)

    server1 = MockServer(sim, latency = 0.1)
    server2 = MockServer(sim, latency = 0.1)

    lb = LoadBalancer(sim)
    lb.addBackend(server1)
    lb.addBackend(server2)

    onShutdownCompleted = Mock()

    def remove_active_server():
        if server1.numSeenRequests:
            lb.removeBackend(server1, onShutdownCompleted)
        else:
            lb.removeBackend(server2, onShutdownCompleted)

    r1 = Request()
    r1.onCompleted = Mock()
    sim.add(0, lambda: lb.request(r1))
    sim.add(1, lambda: remove_active_server())
    sim.add(1, lambda: lb.request(Request()))
    sim.add(2, lambda: lb.request(Request()))
    sim.add(2, lambda: lb.request(Request()))
    sim.run()

    r1.onCompleted.assert_called_once_with()
    onShutdownCompleted.assert_called_once_with()
    assert server1.numSeenRequests == 1 or server2.numSeenRequests == 1
    assert server1.numSeenRequests == 3 or server2.numSeenRequests == 3
def test_without_controller():
    completedRequests = []

    sim = SimulatorKernel(outputDirectory = None)

    server = Server(sim, serviceTimeY = 1, serviceTimeYVariance = 0)

    r = Request()
    r.onCompleted = lambda: completedRequests.append(r)
    sim.add(0, lambda: server.request(r))
    
    r2 = Request()
    r2.onCompleted = lambda: completedRequests.append(r2)
    sim.add(0, lambda: server.request(r2))

    sim.run()

    assert set(completedRequests) == set([ r, r2 ])
    assert server.getActiveTime() == 2.0, server.getActiveTime()
def test_remove_two_servers_while_request_in_progress():
    sim = SimulatorKernel(outputDirectory = None)

    server1 = MockServer(sim, latency = 10)
    server2 = MockServer(sim, latency = 10)
    server3 = MockServer(sim, latency = 10)

    lb = LoadBalancer(sim)
    lb.algorithm = 'SQF'
    lb.addBackend(server1)
    lb.addBackend(server2)
    lb.addBackend(server3)

    onShutdownCompleted = Mock()

    r1 = Request()
    r1.onCompleted = Mock()
    r2 = Request()
    r2.onCompleted = Mock()
    r3 = Request()
    r3.onCompleted = Mock()
    sim.add(0, lambda: lb.request(r1))
    sim.add(0, lambda: lb.request(r2))
    sim.add(0, lambda: lb.request(r3))
    sim.add(1, lambda: lb.removeBackend(server1, onShutdownCompleted))
    sim.add(2, lambda: lb.removeBackend(server2, onShutdownCompleted))
    sim.run()

    r1.onCompleted.assert_called_once_with()
    r2.onCompleted.assert_called_once_with()
    r3.onCompleted.assert_called_once_with()
    assert onShutdownCompleted.call_count == 2
    assert server1.numSeenRequests == 1
    assert server2.numSeenRequests == 1
    assert server3.numSeenRequests == 1
def test_invalid_action():
    sim = SimulatorKernel(outputDirectory = None)

    loadBalancer = MockLoadBalancer(sim, latency = 1)
    autoScalerController = mock.Mock()
    autoScalerController.controlInterval = 1
    autoScalerController.onRequest = mock.Mock(return_value=-2)
    autoScalerController.onCompleted = mock.Mock(return_value=0)
    autoScalerController.onControlPeriod = mock.Mock(return_value=0)
    autoScalerController.onStatus = mock.Mock(return_value=0)

    autoScaler = AutoScaler(sim, loadBalancer, controller = autoScalerController)
    assert str(autoScaler)

    server1 = mock.Mock(name = 'server1')
    server2 = mock.Mock(name = 'server2')
    autoScaler.addBackend(server1)
    autoScaler.addBackend(server2)

    r = Request()
    autoScaler.request(r)
    sim.add(100, lambda: autoScaler.scaleUp())
    sim.run()
def test_random_startup_delay(average=60, variance=10, values_to_test=10):
    rng = random.Random()
    startupDelayFunc = lambda: rng.normalvariate(average, variance)

    # "predict" future random delays by resetting the PRNG's seed
    random_delays = []
    rng.seed(1)
    for _ in range(values_to_test):
        random_delays.append(startupDelayFunc())
    rng.seed(1)

    sim = SimulatorKernel(outputDirectory = None)

    loadBalancer = MockLoadBalancer(sim, latency = 1)
    autoScaler = AutoScaler(sim, loadBalancer, startupDelay = startupDelayFunc)
    assert str(autoScaler)

    for i in range(values_to_test):
        server = mock.Mock(name = 'server' + str(i))
        autoScaler.addBackend(server)

    # NOTE: `lambda i=i` is required to capture the current value of `i` inside
    # the lambda.
    currentTime = 0
    for i in range(values_to_test):
        currentTime += 1
        sim.add(currentTime, lambda i=i: autoScaler.scaleTo(i+1))
        currentTime += random_delays[i]
        sim.add(currentTime-eps, lambda i=i:
                assert_autoscaler_status_is(autoScaler, values_to_test-i-1, 1, i, 0))
        sim.add(currentTime+eps, lambda i=i:
                assert_autoscaler_status_is(autoScaler, values_to_test-i-1, 0, i+1, 0))
    
    sim.run()

    assert_autoscaler_status_is(autoScaler, 0, 0, values_to_test, 0)
def test_scale_up_and_down():
    sim = SimulatorKernel(outputDirectory = None)

    loadBalancer = MockLoadBalancer(sim, latency = 1)
    autoScaler = AutoScaler(sim, loadBalancer, startupDelay = 60)
    assert str(autoScaler)

    server1 = mock.Mock(name = 'server1')
    server2 = mock.Mock(name = 'server2')
    autoScaler.addBackend(server1)
    autoScaler.addBackend(server2)

    sim.add(0, lambda: assert_autoscaler_status_is(autoScaler, 2, 0, 0, 0))

    sim.add(10, lambda: autoScaler.scaleUp())
    sim.add(10, lambda: assert_autoscaler_status_is(autoScaler, 1, 1, 0, 0))

    sim.add(10+59, lambda: loadBalancer.addBackend.assert_not_called()) # startup delay
    sim.add(10+59, lambda: assert_autoscaler_status_is(autoScaler, 1, 1, 0, 0))

    sim.add(10+60+eps, lambda: assert_autoscaler_status_is(autoScaler, 1, 0, 1, 0))
    sim.add(10+60+eps, lambda: loadBalancer.addBackend.assert_called_with(server1))

    sim.add(100, lambda: autoScaler.scaleUp())
    sim.add(100, lambda: assert_autoscaler_status_is(autoScaler, 0, 1, 1, 0))

    sim.add(100+59, lambda: assert_autoscaler_status_is(autoScaler, 0, 1, 1, 0))

    sim.add(100+60+eps, lambda: assert_autoscaler_status_is(autoScaler, 0, 0, 2, 0))
    sim.add(100+60+eps, lambda: loadBalancer.addBackend.assert_called_with(server2))

    sim.add(200, lambda: autoScaler.scaleDown())
    sim.add(200, lambda: assert_autoscaler_status_is(autoScaler, 0, 0, 1, 1))

    sim.add(200+1-eps, lambda: assert_autoscaler_status_is(autoScaler, 0, 0, 1, 1))
    sim.add(200+1+eps, lambda: assert_autoscaler_status_is(autoScaler, 1, 0, 1, 0))

    sim.run()

    assert loadBalancer.lastRemovedBackend == server2
def test_scale_by():
    sim = SimulatorKernel(outputDirectory = None)

    loadBalancer = MockLoadBalancer(sim, latency = 1)
    autoScaler = AutoScaler(sim, loadBalancer, startupDelay = 60)
    assert str(autoScaler)

    server1 = mock.Mock(name = 'server1')
    server2 = mock.Mock(name = 'server2')
    server3 = mock.Mock(name = 'server3')
    server4 = mock.Mock(name = 'server4')
    autoScaler.addBackend(server1)
    autoScaler.addBackend(server2)
    autoScaler.addBackend(server3)
    autoScaler.addBackend(server4)

    sim.add(0, lambda: assert_autoscaler_status_is(autoScaler, 4, 0, 0, 0))

    sim.add(10, lambda: autoScaler.scaleBy(2))
    sim.add(10, lambda: assert_autoscaler_status_is(autoScaler, 2, 2, 0, 0))

    sim.add(10+59, lambda: loadBalancer.addBackend.assert_not_called()) # startup delay
    sim.add(10+59, lambda: assert_autoscaler_status_is(autoScaler, 2, 2, 0, 0))

    sim.add(10+60+eps, lambda: assert_autoscaler_status_is(autoScaler, 2, 0, 2, 0))
    calls1 = [mock.call(server1), mock.call(server2)]
    sim.add(10+60+eps, lambda: loadBalancer.addBackend.assert_has_calls(calls1))

    sim.add(100, lambda: autoScaler.scaleBy(2))
    sim.add(100, lambda: assert_autoscaler_status_is(autoScaler, 0, 2, 2, 0))

    sim.add(100+59, lambda: assert_autoscaler_status_is(autoScaler, 0, 2, 2, 0))

    sim.add(100+60+eps, lambda: assert_autoscaler_status_is(autoScaler, 0, 0, 4, 0))
    calls2 = [mock.call(server3), mock.call(server4)]
    sim.add(100+60+eps, lambda: loadBalancer.addBackend.assert_has_calls(calls2))

    sim.add(200, lambda: autoScaler.scaleBy(-3))
    sim.add(200, lambda: assert_autoscaler_status_is(autoScaler, 0, 0, 1, 3))

    sim.add(200+1-eps, lambda: assert_autoscaler_status_is(autoScaler, 0, 0, 1, 3))
    sim.add(200+1+eps, lambda: assert_autoscaler_status_is(autoScaler, 3, 0, 1, 0))

    sim.run()

    assert loadBalancer.lastRemovedBackend == server2