package com.googlecode.cqengine.persistence.disk;

import com.googlecode.cqengine.ConcurrentIndexedCollection;
import com.googlecode.cqengine.IndexedCollection;
import com.googlecode.cqengine.attribute.SimpleAttribute;
import org.junit.Assert;
import org.junit.Test;

import java.io.File;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import static com.googlecode.cqengine.query.QueryFactory.attribute;
import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * Unit tests for the {@code shared_cache} mode of {@link DiskPersistence}.
 */
public class DiskSharedCacheConcurrencyTest {

    /**
     * Unit test which demonstrates the value of using a read-write lock when {@code shared_cache} mode is enabled.
     * <p>
     * This fails if {@code shared_cache} is enabled while {@code use_read_write_lock} is disabled,
     * and it passes when {@code shared_cache} is enabled and {@code use_read_write_lock} is also enabled.
     */
    @Test
    public void testDiskSharedCacheConcurrency() throws InterruptedException {
        Properties properties = new Properties();
        properties.setProperty("shared_cache", "true");
        properties.setProperty("use_read_write_lock", "true");

        final int NUM_BACKGROUND_THREADS = 4;

        // Integer generator...
        final AtomicInteger idCounter = new AtomicInteger(0);

        // A POJO class, where every instance has a different id generated by the integer generator above...
        class TestPojo {
            Integer id = idCounter.incrementAndGet();
        }

        // An attribute which reads the id field of the POJO...
        final SimpleAttribute<TestPojo, Integer> primaryKey = attribute(object -> object.id);

        // Create a temp file...
        File tempFile = DiskPersistence.createTempFile();

        // Create a collection of TestPojo objects which persists to the temp file...
        IndexedCollection<TestPojo> collection = new ConcurrentIndexedCollection<>(
                DiskPersistence.onPrimaryKeyInFileWithProperties(primaryKey, tempFile, properties)
        );

        // Prepare a latch which will tell us when all background threads have finished executing...
        final CountDownLatch latch = new CountDownLatch(NUM_BACKGROUND_THREADS);

        // Start the background threads...
        for (int i = 0; i < NUM_BACKGROUND_THREADS; i++) {
            new Thread(() -> {
                // In each background thread, add one new object to the collection...
                try {
                    collection.add(new TestPojo());
                }
                catch (Exception ignore) { }
                finally {
                    latch.countDown();
                }
            }).start();
        }
        // Wait for all background threads to finish...
        latch.await(20, SECONDS);

        // Assert that the same number of objects were inserted as there were background threads...
        Assert.assertEquals(NUM_BACKGROUND_THREADS, collection.size());

        // Delete the temp file...
        Assert.assertEquals(true, tempFile.delete());
    }
}
