import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

import java.util.stream.IntStream;

import static org.junit.jupiter.api.Assertions.*;

public class ThreadSafeSnapListTest {
  @Test
  @Timeout(2_000)
  public void testAdd() throws InterruptedException {
    var snapList = new ThreadSafeSnapList<Integer>(__ -> fail());
    var threads = IntStream.range(0, 4)
        .mapToObj(i -> new Thread(() -> {
          for(var v = 0; v < 1_000_000; v++) {
            snapList.add(v);
          }
        }))
        .toList();
    threads.forEach(Thread::start);
    for(var thread: threads) {
      thread.join();
    }
    assertEquals(4_000_000, snapList.elementSize());
  }

  @Test
  @Timeout(1_000)
  public void testToString() throws InterruptedException {
    var snapList = new ThreadSafeSnapList<String>(__ -> fail());
    var threads = IntStream.range(0, 4)
        .mapToObj(i -> new Thread(() -> {
          for(var v = 0; v < 1_000; v++) {
            snapList.add("x");
            var text = snapList.toString();
            if (text.contains("null")) {
              fail("contains null " + text);
            }
          }
        }))
        .toList();
    threads.forEach(Thread::start);
    for(var thread: threads) {
      thread.join();
    }
    assertEquals(4_000, snapList.elementSize());
  }

  @Test
  @Timeout(2_000)
  public void testSnapshot() throws InterruptedException {
    var snapList = new ThreadSafeSnapList<Integer>(list -> list.get(0));
    var threads = IntStream.range(0, 4)
        .mapToObj(i -> new Thread(() -> {
          for(var v = 0; v < 1_000_000; v++) {
            snapList.add(v);
          }
        }))
        .toList();
    var summaryThread = new Thread(() -> {
      while (snapList.elementSize() != 4_000_000) {
        if (snapList.canSnapshot()) {
          snapList.snapshot();
        }
      }
    });
    threads.forEach(Thread::start);
    summaryThread.start();
    for(var thread: threads) {
      thread.join();
    }
    summaryThread.join();
    assertEquals(4_000_000, snapList.elementSize());
  }
}