package com.codingchili.core.context;

import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.*;
import org.junit.runner.RunWith;

import java.util.concurrent.atomic.AtomicInteger;

import com.codingchili.core.configuration.system.SystemSettings;
import com.codingchili.core.testing.ContextMock;

/**
 * @author Robin Duda
 *
 * Tests the timers and metrics in the system context.
 */
@RunWith(VertxUnitRunner.class)
public class SystemContextTest {
    private SystemContext context;
    private OnMetricListener listener;
    private SystemSettings settings;

    @Before
    public void setUp() {
        this.settings = new SystemSettings();

        settings.setMetricRate(100);
        settings.setMetrics(true);

        this.context = new ContextMock(Vertx.vertx()) {
            @Override
            protected void onMetricsSnapshot(JsonObject json) {
                if (listener != null) {
                    listener.onMetric(json);
                }
            }

            @Override
            public SystemSettings system() {
                return settings;
            }
        };
        Delay.initialize(context);
    }

    @After
    public void tearDown(TestContext test) {
        context.vertx.close(test.asyncAssertSuccess());
    }

    @Test
    public void testMetricsEnabled(TestContext test) {
        Async async = test.async();

        this.listener = metrics -> {
            async.complete();
        };
    }

    @Test
    public void testMetricsDisabled(TestContext test) {
        Async async = test.async();

        settings.setMetrics(false);
        this.listener = metrics -> test.fail("Metrics was not disabled.");

        Delay.forMS(async, settings.getMetricRate() * 2);
    }

    @Test
    public void testCancelTimer(TestContext test) {
        Async async = test.async();

        long id = context.timer(settings.getMetricRate(), handler -> {
            test.fail("The timer was not cancelled.");
        });

        context.cancel(id);

        Delay.forMS(async, settings.getMetricRate() * 2);
    }

    @Test
    public void testTimerCalled(TestContext test) {
        Async async = test.async();
        context.timer(1, event -> async.complete());
    }

    @Test
    public void testPeriodic(TestContext test) {
        Async async = test.async();
        int[] countdown = {3};
        int interval = 25;

        context.periodic(() -> interval, "test", event -> {
            countdown[0]--;
        });

        context.timer(90, event -> {
            test.assertTrue(0 == countdown[0]);
            async.complete();
        });
    }

    @Test
    public void testPeriodicIntervalChanged(TestContext test) {
        Async async = test.async();
        AtomicInteger countdown = new AtomicInteger(3);
        AtomicInteger interval = new AtomicInteger(50);

        // assert the periodic is only triggered twice.
        context.timer(400, event -> {
            test.assertEquals(1, countdown.get());
            async.complete();
        });

        // executes once, timer changes, executes again, notices change.
        context.periodic(interval::get, "test", event -> {
            interval.set(1000);
            countdown.decrementAndGet();
        });
    }

    @FunctionalInterface
    interface OnMetricListener {
        void onMetric(JsonObject metrics);
    }
}

@@ -168,16 +168,55 @@
 ontext;%0A
+import io.vertx.ext.unit.junit.Repeat;%0A
 import i
@@ -2508,29 +2508,54 @@
     
-int%5B%5D countdown = %7B3%7D
+AtomicInteger countdown = new AtomicInteger(3)
 ;%0A  
@@ -2579,10 +2579,10 @@
 l = 
-2
 5
+0
 ;%0A%0A 
@@ -2665,13 +2665,26 @@
 down
-%5B0%5D--
+.getAndDecrement()
 ;%0A  
@@ -2720,10 +2720,11 @@
 mer(
-90
+185
 , ev
@@ -2778,11 +2778,14 @@
 down
-%5B0%5D
+.get()
 );%0A 

Update testPeriodic test.