@@ -0,0 +1,2287 @@
+package com.github.bjoernpetersen.jmusicbot.playback;%0A%0Aimport static java.time.Duration.ofMillis;%0Aimport static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;%0Aimport static org.junit.jupiter.api.Assertions.assertTrue;%0A%0Aimport java.util.Arrays;%0Aimport java.util.List;%0Aimport java.util.concurrent.atomic.AtomicBoolean;%0Aimport java.util.concurrent.locks.Condition;%0Aimport java.util.concurrent.locks.Lock;%0Aimport java.util.concurrent.locks.ReentrantLock;%0Aimport org.junit.jupiter.api.Assertions;%0Aimport org.junit.jupiter.api.BeforeEach;%0Aimport org.junit.jupiter.api.Test;%0Aimport org.junit.jupiter.params.ParameterizedTest;%0Aimport org.junit.jupiter.params.provider.MethodSource;%0A%0Aclass AbstractPlaybackTest %7B%0A%0A  private AbstractPlayback uut;%0A%0A  @BeforeEach%0A  void init() %7B%0A    uut = new TestPlayback();%0A  %7D%0A%0A  @Test%0A  void waitIndefinitely() %7B%0A    Assertions.assertThrows(AssertionError.class,%0A        () -%3E assertTimeoutPreemptively(ofMillis(500), () -%3E uut.waitForFinish())%0A    );%0A  %7D%0A%0A  private static List%3CCallback%3CAbstractPlayback%3E%3E doneCalls() %7B%0A    return Arrays.asList(AbstractPlayback::markDone, AbstractPlayback::close);%0A  %7D%0A%0A  @ParameterizedTest%0A  @MethodSource(names = %22doneCalls%22)%0A  void waitFinishesOnDone(Callback%3CAbstractPlayback%3E doneCall) throws InterruptedException %7B%0A    Lock lock = new ReentrantLock();%0A    AtomicBoolean success = new AtomicBoolean(false);%0A    Condition called = lock.newCondition();%0A    Thread waiter = new Thread(() -%3E %7B%0A      try %7B%0A        uut.waitForFinish();%0A        success.set(true);%0A      %7D catch (InterruptedException e) %7B%0A        // ignore, leave success on false%0A      %7D finally %7B%0A        lock.lock();%0A        try %7B%0A          called.signalAll();%0A        %7D finally %7B%0A          lock.unlock();%0A        %7D%0A      %7D%0A    %7D);%0A    waiter.start();%0A%0A    lock.lock();%0A    try %7B%0A      assertTimeoutPreemptively(ofMillis(500), () -%3E doneCall.call(uut));%0A      called.await();%0A    %7D finally %7B%0A      lock.unlock();%0A      waiter.interrupt();%0A    %7D%0A%0A    assertTrue(success.get());%0A  %7D%0A%0A  @FunctionalInterface%0A  private interface Callback%3CT%3E %7B%0A%0A    void call(T t) throws Exception;%0A  %7D%0A%0A%0A  private static class TestPlayback extends AbstractPlayback %7B%0A%0A    @Override%0A    public void play() %7B%0A    %7D%0A%0A    @Override%0A    public void pause() %7B%0A    %7D%0A  %7D%0A%7D%0A

Add AbstractPlayback tests