package anastore.client;

import anastore.util.Bug;

import java.util.*;

import org.junit.*;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import static org.junit.Assert.*;

@org.junit.runner.RunWith(RWStateTest.Runner.class)
public class RWStateTest
{
    public static class Runner extends org.junit.runner.Runner
    {
        public Runner(Class testClass)
        {
            if (testClass != RWStateTest.class)
                throw new IllegalArgumentException();
        }

        public Description getDescription()
        {
            return Description.createSuiteDescription(RWStateTest.class);
        }

        public void run(RunNotifier notifier)
        {
            for (RWState.State from : RWState.State.values())
                for (RWState.State to : RWState.State.values()) {
                    RWStateTest test = new RWStateTest();
                    String name = (from.toString().toLowerCase() + "To" +
                                   to.toString().substring(0, 1) +
                                   to.toString().substring(1).toLowerCase());
                    Description desc =
                        Description.createTestDescription(RWStateTest.class,
                                                          name);
                    notifier.fireTestStarted(desc);
                    try {
                        test.transition(from, to);
                    } catch (Exception e) {
                        notifier.fireTestFailure(new Failure(desc, e));
                    }
                    notifier.fireTestFinished(desc);
                }
        }
    }

    private static final RWState.State[][] ALLOWED =
    {{RWState.ACTIVE, RWState.COMMITTING},
     {RWState.ACTIVE, RWState.COMMITTED},
     {RWState.COMMITTING, RWState.COMMITTED},
     {RWState.ACTIVE, RWState.ABORTED},
     {RWState.COMMITTING, RWState.ABORTED},
     {RWState.ABORTED, RWState.ABORTED}};

    public void transition(RWState.State from, RWState.State to)
        throws AbortedException
    {
        RWState state = new RWState();
        RWState.State goal;

        if (from != RWState.ACTIVE)
            state.set(from);

        if (to == RWState.ACTIVE) {
            try {
                state.set(to);
                fail("Expected IllegalArgumentException");
            } catch (IllegalArgumentException e) {
                // Good
            }
            goal = from;
        } else {
            boolean allowed = false;

            // Is it allowed?
            for (RWState.State[] allow : ALLOWED) {
                if (allow[0] == from && allow[1] == to) {
                    allowed = true;
                    break;
                }
            }

            if (allowed) {
                state.set(to);
                goal = to;
            } else {
                try {
                    state.set(to);
                    fail("Expected IllegalStateException");
                } catch (IllegalStateException e) {
                    // Good
                }
                goal = from;
            }
        }

        if (goal == RWState.ACTIVE) {
            state.checkActive();
        } else if (goal == RWState.ABORTED) {
            try {
                state.checkActive();
                fail("Expected AbortedException");
            } catch (AbortedException e) {
                // Good
            }
        } else {
            try {
                state.checkActive();
                fail("Expected IllegalStateException");
            } catch (IllegalStateException e) {
                // Good
            }
        }
    }
}
