// Path: src/test/java/net/bourgau/philippe/concurrency/kata/Implementations.java
// public class Implementations {
// 
//     public static Collection<Object[]> all() {
//         return simple(
//                 new MonoThread(),
//                 new UnboundedSync(),
//                 new UnboundedConcurrent(),
//                 new BoundedConcurrent(),
//                 new BoundedFineGrained(),
//                 new ActorsRealThreads(),
//                 new ActorsGreenThreads(),
//                 new CSP());
//     }
// 
//     public static Collection<Object[]> bounded() {
//         return simple(
//                 new MonoThread(),
//                 new BoundedConcurrent(),
//                 new BoundedFineGrained(),
//                 new ActorsGreenThreads(),
//                 new CSP());
//     }
// 
//     public static Collection<Object[]> multithreaded() {
//         return simple(
//                 new UnboundedSync(),
//                 new UnboundedConcurrent(),
//                 new BoundedConcurrent(),
//                 new BoundedFineGrained(),
//                 new ActorsRealThreads(),
//                 new ActorsGreenThreads(),
//                 new CSP());
//     }
// 
//     public static Collection<Object[]> simple(Object... values) {
//         ArrayList<Object[]> parameters = new ArrayList<>();
//         for (Object value : values) {
//             parameters.add(new Object[]{value});
//         }
//         return parameters;
//     }
// 
//     public static Collection<Object[]> complex(Object[]... objects) {
//         ArrayList<Object[]> parameters = new ArrayList<>();
//         for (Object[] object : objects) {
//             parameters.add(object);
//         }
//         return parameters;
//     }
// 
//     public static Collection<Object[]> crossProduct(Collection<Object[]> xs, Collection<Object[]> ys) {
//         ArrayList<Object[]> parameters = new ArrayList<>();
//         for (Object[] x : xs) {
//             for (Object[] y : ys) {
//                 parameters.add(ArrayUtils.addAll(x, y));
//             }
//         }
//         return parameters;
//     }
// }
// 
// Path: src/test/java/net/bourgau/philippe/concurrency/kata/MemoryOutput.java
// public class MemoryOutput implements Output {
// 
//     private final StringBuilder text;
// 
//     public MemoryOutput() {
//         text = new StringBuilder();
//     }
// 
//     public void write(String line) {
//         text.append(line).append("\n");
//     }
// 
//     @Override
//     public String toString() {
//         return text.toString();
//     }
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/ChatRoom.java
// public interface ChatRoom {
//     String GOD_PREFIX = "QPHILIPPEZDFSDFQSRULESDHQSHQS:";
// 
//     void enter(Output client, String pseudo);
// 
//     void broadcast(Output client, String message);
// 
//     void leave(Output client);
// 
//     boolean waitForAbandon(long count, TimeUnit timeUnit) throws InterruptedException;
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/Client.java
// public interface Client extends Output {
//     void enter() throws Exception;
// 
//     void announce(String message);
// 
//     void leave() throws Exception;
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/Implementation.java
// public interface Implementation {
//     ChatRoom startNewChatRoom();
// 
//     Client newClient(String name, ChatRoom chatRoom, Output out);
// 
//     void awaitOrShutdown(int count, TimeUnit timeUnit) throws InterruptedException;
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/Output.java
// public interface Output {
//     void write(String line);
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/Message.java
// public final class Message {
//     public static String welcome(String pseudo) {
//         return String.format("Welcome %s !", pseudo);
//     }
// 
//     public static String signed(String name, String message) {
//         return String.format("%s > %s", name, message);
//     }
// 
//     public static String exit(String name) {
//         return String.format("%s left", name);
//     }
// 
//     public static String selfExit() {
//         return "You left the room";
//     }
// 
//     public static String login(String message) {
//         return ChatRoom.GOD_PREFIX + message;
//     }
// }


import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.core.ConditionFactory;
import com.jayway.awaitility.core.ConditionTimeoutException;
import net.bourgau.philippe.concurrency.kata.Implementations;
import net.bourgau.philippe.concurrency.kata.MemoryOutput;
import net.bourgau.philippe.concurrency.kata.common.ChatRoom;
import net.bourgau.philippe.concurrency.kata.common.Client;
import net.bourgau.philippe.concurrency.kata.common.Implementation;
import net.bourgau.philippe.concurrency.kata.common.Output;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Collection;
import static java.util.concurrent.TimeUnit.SECONDS;
import static net.bourgau.philippe.concurrency.kata.common.Message.*;
import static org.fest.assertions.api.Assertions.assertThat;
    a_client_receives_its_own_welcome_message() throws Exception {
        joeShouldReceive(welcome("Joe"));
    }

    @Test
    public void
    a_client_receives_its_own_messages() throws Exception {
        joe.announce("Hi everyone !");

        joeShouldReceive(signed("Joe", "Hi everyone !"));
    }

    @Test
    public void
    a_client_is_notified_of_newcomers() throws Exception {
        jack.enter();

        joeShouldReceive(welcome("Jack"));
    }

    @Test
    public void
    two_clients_can_chat_together() throws Exception {
        jack.enter();
        jack.announce("Hi there !");

        joeShouldReceive(signed("Jack", "Hi there !"));
    }

    @Test
// Path: src/test/java/net/bourgau/philippe/concurrency/kata/Implementations.java
// public class Implementations {
// 
//     public static Collection<Object[]> all() {
//         return simple(
//                 new MonoThread(),
//                 new UnboundedSync(),
//                 new UnboundedConcurrent(),
//                 new BoundedConcurrent(),
//                 new BoundedFineGrained(),
//                 new ActorsRealThreads(),
//                 new ActorsGreenThreads(),
//                 new CSP());
//     }
// 
//     public static Collection<Object[]> bounded() {
//         return simple(
//                 new MonoThread(),
//                 new BoundedConcurrent(),
//                 new BoundedFineGrained(),
//                 new ActorsGreenThreads(),
//                 new CSP());
//     }
// 
//     public static Collection<Object[]> multithreaded() {
//         return simple(
//                 new UnboundedSync(),
//                 new UnboundedConcurrent(),
//                 new BoundedConcurrent(),
//                 new BoundedFineGrained(),
//                 new ActorsRealThreads(),
//                 new ActorsGreenThreads(),
//                 new CSP());
//     }
// 
//     public static Collection<Object[]> simple(Object... values) {
//         ArrayList<Object[]> parameters = new ArrayList<>();
//         for (Object value : values) {
//             parameters.add(new Object[]{value});
//         }
//         return parameters;
//     }
// 
//     public static Collection<Object[]> complex(Object[]... objects) {
//         ArrayList<Object[]> parameters = new ArrayList<>();
//         for (Object[] object : objects) {
//             parameters.add(object);
//         }
//         return parameters;
//     }
// 
//     public static Collection<Object[]> crossProduct(Collection<Object[]> xs, Collection<Object[]> ys) {
//         ArrayList<Object[]> parameters = new ArrayList<>();
//         for (Object[] x : xs) {
//             for (Object[] y : ys) {
//                 parameters.add(ArrayUtils.addAll(x, y));
//             }
//         }
//         return parameters;
//     }
// }
// 
// Path: src/test/java/net/bourgau/philippe/concurrency/kata/MemoryOutput.java
// public class MemoryOutput implements Output {
// 
//     private final StringBuilder text;
// 
//     public MemoryOutput() {
//         text = new StringBuilder();
//     }
// 
//     public void write(String line) {
//         text.append(line).append("\n");
//     }
// 
//     @Override
//     public String toString() {
//         return text.toString();
//     }
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/ChatRoom.java
// public interface ChatRoom {
//     String GOD_PREFIX = "QPHILIPPEZDFSDFQSRULESDHQSHQS:";
// 
//     void enter(Output client, String pseudo);
// 
//     void broadcast(Output client, String message);
// 
//     void leave(Output client);
// 
//     boolean waitForAbandon(long count, TimeUnit timeUnit) throws InterruptedException;
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/Client.java
// public interface Client extends Output {
//     void enter() throws Exception;
// 
//     void announce(String message);
// 
//     void leave() throws Exception;
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/Implementation.java
// public interface Implementation {
//     ChatRoom startNewChatRoom();
// 
//     Client newClient(String name, ChatRoom chatRoom, Output out);
// 
//     void awaitOrShutdown(int count, TimeUnit timeUnit) throws InterruptedException;
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/Output.java
// public interface Output {
//     void write(String line);
// }
// 
// Path: src/main/java/net/bourgau/philippe/concurrency/kata/common/Message.java
// public final class Message {
//     public static String welcome(String pseudo) {
//         return String.format("Welcome %s !", pseudo);
//     }
// 
//     public static String signed(String name, String message) {
//         return String.format("%s > %s", name, message);
//     }
// 
//     public static String exit(String name) {
//         return String.format("%s left", name);
//     }
// 
//     public static String selfExit() {
//         return "You left the room";
//     }
// 
//     public static String login(String message) {
//         return ChatRoom.GOD_PREFIX + message;
//     }
// }


// Path: src/test/java/net/bourgau/philippe/concurrency/kata/tests/EndToEndTest.java
import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.core.ConditionFactory;
import com.jayway.awaitility.core.ConditionTimeoutException;
import net.bourgau.philippe.concurrency.kata.Implementations;
import net.bourgau.philippe.concurrency.kata.MemoryOutput;
import net.bourgau.philippe.concurrency.kata.common.ChatRoom;
import net.bourgau.philippe.concurrency.kata.common.Client;
import net.bourgau.philippe.concurrency.kata.common.Implementation;
import net.bourgau.philippe.concurrency.kata.common.Output;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Collection;
import static java.util.concurrent.TimeUnit.SECONDS;
import static net.bourgau.philippe.concurrency.kata.common.Message.*;
import static org.fest.assertions.api.Assertions.assertThat;

    a_client_receives_its_own_welcome_message() throws Exception {
        joeShouldReceive(welcome("Joe"));
    }

    @Test
    public void
    a_client_receives_its_own_messages() throws Exception {
        joe.announce("Hi everyone !");

        joeShouldReceive(signed("Joe", "Hi everyone !"));
    }

    @Test
    public void
    a_client_is_notified_of_newcomers() throws Exception {
        jack.enter();

        joeShouldReceive(welcome("Jack"));
    }

    @Test
    public void
    two_clients_can_chat_together() throws Exception {
        jack.enter();
        jack.announce("Hi there !");

        joeShouldReceive(signed("Jack", "Hi there !"));
    }

    @Test
    public void