package com.cookpad.puree.outputs; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.cookpad.puree.PureeConfiguration; import com.cookpad.puree.PureeFilter; import com.cookpad.puree.PureeLog; import com.cookpad.puree.PureeLogger; import com.cookpad.puree.async.AsyncResult; import junit.framework.AssertionFailedError; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; @RunWith(AndroidJUnit4.class) public class PureeBufferedOutputTest { Context context; BlockingQueue<String> logs = new ArrayBlockingQueue<>(3); PureeLogger logger; static class PvLog implements PureeLog { String name; public PvLog(String name) { this.name = name; } } static class FooLog implements PureeLog { String name; public FooLog(String name) { this.name = name; } } static class BarLog implements PureeLog { String name; public BarLog(String name) { this.name = name; } } static class BazLog implements PureeLog { String name; public BazLog(String name) { this.name = name; } } @ParametersAreNonnullByDefault static abstract class BufferedOutputBase extends PureeBufferedOutput { @Nonnull @Override public String type() { return "buffered_output"; } @Nonnull @Override public OutputConfiguration configure(OutputConfiguration conf) { conf.setFlushIntervalMillis(10); return conf; } } @ParametersAreNonnullByDefault class BufferedOutput extends BufferedOutputBase { int flushCount = 0; @Override public void emit(JsonArray jsonArray, AsyncResult result) { for (JsonElement item : jsonArray) { logs.add(item.toString()); } result.success(); } @Override public void flush() { flushCount++; super.flush(); } } @ParametersAreNonnullByDefault class BufferedOutputAsync extends BufferedOutput { int flushCount = 0; @Override public void emit(JsonArray jsonArray, final AsyncResult result) { for (final JsonElement item : jsonArray) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { } logs.add(item.toString()); result.success(); } }).start(); } } @Override public void flush() { flushCount++; super.flush(); } } @ParametersAreNonnullByDefault class TruncateBufferedOutput extends BufferedOutput { @Nonnull @Override public OutputConfiguration configure(OutputConfiguration conf) { conf.setFlushIntervalMillis(5000); return conf; } } @ParametersAreNonnullByDefault class DiscardedBufferedOutput extends BufferedOutputBase { @Override public void emit(JsonArray jsonArray, AsyncResult result) { throw new AssertionFailedError("not reached"); } } @ParametersAreNonnullByDefault class BufferedOutputToTestFailFirst extends BufferedOutputBase { int counter = 0; @Override public void emit(JsonArray jsonArray, AsyncResult result) { if (counter++ == 0) { result.fail(); } else { for (JsonElement item : jsonArray) { logs.add(item.toString()); } result.success(); } } } @ParametersAreNonnullByDefault public static class DiscardFilter implements PureeFilter { @Nullable @Override public JsonObject apply(JsonObject jsonLog) { return null; } } @Before public void setUp() throws Exception { context = InstrumentationRegistry.getTargetContext(); } @After public void tearDown() throws Exception { if (logger != null) { logger.discardBufferedLogs(); } } void initializeLogger(PureeOutput output) { logger = new PureeConfiguration.Builder(context) .register(PvLog.class, output) .build() .createPureeLogger(); logger.discardBufferedLogs(); } @Test public void testPureeBufferedOutput() throws Exception { initializeLogger(new BufferedOutput()); logger.send(new PvLog("foo")); logger.send(new PvLog("bar")); logger.send(new PvLog("baz")); logger.flush(); Thread.sleep(100); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is("{\"name\":\"foo\"}")); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is("{\"name\":\"bar\"}")); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is("{\"name\":\"baz\"}")); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is(nullValue())); } @Test public void testPureeBufferedOutputWithDiscardFilter() throws Exception { initializeLogger(new DiscardedBufferedOutput().withFilters(new DiscardFilter())); logger.send(new PvLog("foo")); logger.send(new PvLog("bar")); logger.send(new PvLog("baz")); logger.flush(); Thread.sleep(100); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is(nullValue())); } @Test public void testBufferedOutputToTestFailFirst() throws Exception { BufferedOutputToTestFailFirst output = new BufferedOutputToTestFailFirst(); initializeLogger(output); logger.send(new PvLog("foo")); logger.send(new PvLog("bar")); logger.send(new PvLog("baz")); logger.flush(); Thread.sleep(100); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is("{\"name\":\"foo\"}")); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is("{\"name\":\"bar\"}")); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is("{\"name\":\"baz\"}")); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is(nullValue())); } @Test public void testTruncateBufferedLogs() throws Exception { initializeLogger(new TruncateBufferedOutput()); logger.send(new PvLog("foo")); logger.send(new PvLog("bar")); logger.send(new PvLog("baz")); Thread.sleep(100); logger.truncateBufferedLogs(2); logger.flush(); Thread.sleep(100); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is("{\"name\":\"bar\"}")); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is("{\"name\":\"baz\"}")); assertThat(logs.poll(100, TimeUnit.MILLISECONDS), is(nullValue())); } @Test public void testPureeBufferedOutput_countFlush() throws Exception { BufferedOutput output = new BufferedOutput(); logger = new PureeConfiguration.Builder(context) .register(PvLog.class, output) .register(FooLog.class, output) .register(BarLog.class, output) .register(BazLog.class, output) .build() .createPureeLogger(); logger.send(new PvLog("foo")); logger.send(new PvLog("bar")); logger.send(new PvLog("baz")); logger.flush(); Thread.sleep(100); assertThat(output.flushCount, is(lessThanOrEqualTo(2))); } @Test public void testPureeBufferedOutput_countEmit() throws Exception { BufferedOutput output = new BufferedOutput(); logger = new PureeConfiguration.Builder(context) .register(PvLog.class, output) .register(FooLog.class, output) .register(BarLog.class, output) .register(BazLog.class, output) .build() .createPureeLogger(); logger.send(new PvLog("foo")); logger.send(new PvLog("bar")); logger.send(new PvLog("baz")); logger.flush(); Thread.sleep(100); assertThat(logs.size(), is(lessThanOrEqualTo(3))); } @Test public void testPureeBufferedOutput_countEmitAsync() throws Exception { BufferedOutputAsync output = new BufferedOutputAsync(); logger = new PureeConfiguration.Builder(context) .register(PvLog.class, output) .register(FooLog.class, output) .register(BarLog.class, output) .register(BazLog.class, output) .build() .createPureeLogger(); logger.flush(); Thread.sleep(100); assertThat(logs.size(), is(0)); logger.send(new FooLog("foo")); logger.send(new BarLog("bar")); logger.send(new BazLog("baz")); logger.flush(); Thread.sleep(1000); assertThat(logs.size(), is(3)); } }