/*
* Copyright 2016 Ben Manes. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.benmanes.caffeine.cache;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import java.util.concurrent.atomic.AtomicInteger;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.github.benmanes.caffeine.testing.Awaits;
import com.github.benmanes.caffeine.testing.ConcurrentTestHarness;
/**
* @author ben.manes@gmail.com (Ben Manes)
*/
public final class MpscGrowableArrayQueueTest {
private static final int NUM_PRODUCERS = 10;
private static final int PRODUCE = 100;
private static final int POPULATED_SIZE = 10;
private static final int FULL_SIZE = 32;
/* ---------------- Size -------------- */
@Test(dataProvider = "empty")
public void size_whenEmpty(MpscGrowableArrayQueue<Integer> buffer) {
assertThat(buffer.size(), is(0));
}
@Test(dataProvider = "populated")
public void size_whenPopulated(MpscGrowableArrayQueue<Integer> buffer) {
assertThat(buffer.size(), is(POPULATED_SIZE));
}
/* ---------------- Offer -------------- */
@Test(dataProvider = "empty")
public void offer_whenEmpty(MpscGrowableArrayQueue<Integer> buffer) {
assertThat(buffer.offer(1), is(true));
assertThat(buffer.size(), is(1));
}
@Test(dataProvider = "populated")
public void offer_whenPopulated(MpscGrowableArrayQueue<Integer> buffer) {
assertThat(buffer.offer(1), is(true));
assertThat(buffer.size(), is(POPULATED_SIZE + 1));
}
@Test(dataProvider = "full")
public void offer_whenFull(MpscGrowableArrayQueue<Integer> buffer) {
assertThat(buffer.offer(1), is(false));
assertThat(buffer.size(), is(FULL_SIZE));
}
/* ---------------- Poll -------------- */
@Test(dataProvider = "empty")
public void poll_whenEmpty(MpscGrowableArrayQueue<Integer> buffer) {
assertThat(buffer.poll(), is(nullValue()));
}
@Test(dataProvider = "populated")
public void poll_whenPopulated(MpscGrowableArrayQueue<Integer> buffer) {
assertThat(buffer.poll(), is(not(nullValue())));
assertThat(buffer.size(), is(POPULATED_SIZE - 1));
}
@Test(dataProvider = "full")
public void poll_toEmpty(MpscGrowableArrayQueue<Integer> buffer) {
while (buffer.poll() != null) {}
assertThat(buffer.size(), is(0));
}
/* ---------------- Concurrency -------------- */
@Test(dataProvider = "empty")
public void oneProducer_oneConsumer(MpscGrowableArrayQueue<Integer> buffer) {
AtomicInteger started = new AtomicInteger();
AtomicInteger finished = new AtomicInteger();
ConcurrentTestHarness.execute(() -> {
started.incrementAndGet();
Awaits.await().untilAtomic(started, is(2));
for (int i = 0; i < PRODUCE; i++) {
while (!buffer.offer(i)) {}
}
finished.incrementAndGet();
});
ConcurrentTestHarness.execute(() -> {
started.incrementAndGet();
Awaits.await().untilAtomic(started, is(2));
for (int i = 0; i < PRODUCE; i++) {
while (buffer.poll() == null) {}
}
finished.incrementAndGet();
});
Awaits.await().untilAtomic(finished, is(2));
assertThat(buffer.size(), is(0));
}
@Test(dataProvider = "empty")
public void manyProducers_noConsumer(MpscGrowableArrayQueue<Integer> buffer) {
AtomicInteger count = new AtomicInteger();
ConcurrentTestHarness.timeTasks(NUM_PRODUCERS, () -> {
for (int i = 0; i < PRODUCE; i++) {
if (buffer.offer(i)) {
count.incrementAndGet();
}
}
});
assertThat(buffer.size(), is(count.get()));
}
@Test(dataProvider = "empty")
public void manyProducers_oneConsumer(MpscGrowableArrayQueue<Integer> buffer) {
AtomicInteger started = new AtomicInteger();
AtomicInteger finished = new AtomicInteger();
ConcurrentTestHarness.execute(() -> {
started.incrementAndGet();
Awaits.await().untilAtomic(started, is(NUM_PRODUCERS + 1));
for (int i = 0; i < (NUM_PRODUCERS * PRODUCE); i++) {
while (buffer.poll() == null) {}
}
finished.incrementAndGet();
});
ConcurrentTestHarness.timeTasks(NUM_PRODUCERS, () -> {
started.incrementAndGet();
Awaits.await().untilAtomic(started, is(NUM_PRODUCERS + 1));
for (int i = 0; i < PRODUCE; i++) {
while (!buffer.offer(i)) {}
}
finished.incrementAndGet();
});
Awaits.await().untilAtomic(finished, is(NUM_PRODUCERS + 1));
assertThat(buffer.size(), is(0));
}
/* ---------------- Providers -------------- */
@DataProvider(name = "empty")
public Object[][] providesEmpty() {
return new Object[][] {{ makePopulated(0) }};
}
@DataProvider(name = "populated")
public Object[][] providesPopulated() {
return new Object[][] {{ makePopulated(POPULATED_SIZE) }};
}
@DataProvider(name = "full")
public Object[][] providesFull() {
return new Object[][] {{ makePopulated(FULL_SIZE) }};
}
static MpscGrowableArrayQueue<Integer> makePopulated(int items) {
MpscGrowableArrayQueue<Integer> buffer = new MpscGrowableArrayQueue<>(4, FULL_SIZE);
for (int i = 0; i < items; i++) {
buffer.offer(i);
}
return buffer;
}
}