// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.concurrent;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * @author Simon Thoresen Hult
 */
public class EventBarrierTestCase {

    @Test
    public void testEmpty() {
        // waiting for an empty set of events
        Barrier b = new Barrier();
        EventBarrier eb = new EventBarrier();

        assertTrue(!eb.startBarrier(b));
        assertTrue(!b.done);
        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);

        int token = eb.startEvent();
        eb.completeEvent(token);

        assertTrue(!eb.startBarrier(b));
        assertTrue(!b.done);
        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);
    }

    @Test
    public void testSimple() {
        // a single barrier waiting for a single event
        Barrier b = new Barrier();
        EventBarrier eb = new EventBarrier();
        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);

        int token = eb.startEvent();
        assertEquals(eb.getNumEvents(), 1);
        assertEquals(eb.getNumBarriers(), 0);

        assertTrue(eb.startBarrier(b));
        assertTrue(!b.done);
        assertEquals(eb.getNumEvents(), 1);
        assertEquals(eb.getNumBarriers(), 1);

        eb.completeEvent(token);
        assertTrue(b.done);
        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);
    }

    @Test
    public void testBarrierChain() {
        // more than one barrier waiting for the same set of events
        Barrier b1 = new Barrier();
        Barrier b2 = new Barrier();
        Barrier b3 = new Barrier();
        EventBarrier eb = new EventBarrier();
        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);

        int token = eb.startEvent();
        assertEquals(eb.getNumEvents(), 1);
        assertEquals(eb.getNumBarriers(), 0);

        assertTrue(eb.startBarrier(b1));
        assertTrue(eb.startBarrier(b2));
        assertTrue(eb.startBarrier(b3));
        assertTrue(!b1.done);
        assertTrue(!b2.done);
        assertTrue(!b3.done);

        assertEquals(eb.getNumEvents(), 1);
        assertEquals(eb.getNumBarriers(), 3);

        eb.completeEvent(token);
        assertTrue(b1.done);
        assertTrue(b2.done);
        assertTrue(b3.done);
        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);
    }

    @Test
    public void testEventAfter() {
        // new events starting after the start of a barrier
        Barrier b = new Barrier();
        EventBarrier eb = new EventBarrier();
        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);

        int token = eb.startEvent();
        assertEquals(eb.getNumEvents(), 1);
        assertEquals(eb.getNumBarriers(), 0);

        assertTrue(eb.startBarrier(b));
        assertTrue(!b.done);
        assertEquals(eb.getNumEvents(), 1);
        assertEquals(eb.getNumBarriers(), 1);

        int t2 = eb.startEvent();
        assertTrue(!b.done);
        assertEquals(eb.getNumEvents(), 2);
        assertEquals(eb.getNumBarriers(), 1);

        eb.completeEvent(token);
        assertTrue(b.done);
        assertEquals(eb.getNumEvents(), 1);
        assertEquals(eb.getNumBarriers(), 0);

        eb.completeEvent(t2);
        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);
    }

    @Test
    public void testReorder() {
        // events completing in a different order than they started
        Barrier b1 = new Barrier();
        Barrier b2 = new Barrier();
        Barrier b3 = new Barrier();
        EventBarrier eb = new EventBarrier();

        int t1 = eb.startEvent();
        eb.startBarrier(b1);
        int t2 = eb.startEvent();
        eb.startBarrier(b2);
        int t3 = eb.startEvent();
        eb.startBarrier(b3);
        int t4 = eb.startEvent();

        assertEquals(eb.getNumEvents(), 4);
        assertEquals(eb.getNumBarriers(), 3);

        assertTrue(!b1.done);
        assertTrue(!b2.done);
        assertTrue(!b3.done);

        eb.completeEvent(t4);
        assertTrue(!b1.done);
        assertTrue(!b2.done);
        assertTrue(!b3.done);

        eb.completeEvent(t3);
        assertTrue(!b1.done);
        assertTrue(!b2.done);
        assertTrue(!b3.done);

        eb.completeEvent(t1);
        assertTrue(b1.done);
        assertTrue(!b2.done);
        assertTrue(!b3.done);

        eb.completeEvent(t2);
        assertTrue(b1.done);
        assertTrue(b2.done);
        assertTrue(b3.done);

        assertEquals(eb.getNumEvents(), 0);
        assertEquals(eb.getNumBarriers(), 0);
    }

    private static class Barrier implements EventBarrier.BarrierWaiter {
        boolean done = false;

        @Override
        public void completeBarrier() {
            done = true;
        }
    }

}
