package org.gridkit.jvmtool.codec.stacktrace;
import static org.assertj.core.api.Assertions.assertThat;
import static org.gridkit.jvmtool.codec.stacktrace.EventEqualToCondition.eventEquals;
import static org.gridkit.jvmtool.codec.stacktrace.EventSeqEqualToCondition.exactlyAs;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.Thread.State;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import org.gridkit.jvmtool.event.CommonEvent;
import org.gridkit.jvmtool.event.ErrorEvent;
import org.gridkit.jvmtool.event.Event;
import org.gridkit.jvmtool.event.EventReader;
import org.gridkit.jvmtool.event.MorphingEventReader;
import org.gridkit.jvmtool.event.SimpleTagCollection;
import org.gridkit.jvmtool.event.TagCollection;
import org.gridkit.jvmtool.event.UniversalEventWriter;
import org.gridkit.jvmtool.stacktrace.CounterCollection;
import org.gridkit.jvmtool.stacktrace.StackFrame;
import org.gridkit.jvmtool.stacktrace.StackFrameArray;
import org.gridkit.jvmtool.stacktrace.StackFrameList;
import org.gridkit.jvmtool.stacktrace.ThreadEventCodec;
import org.junit.Test;
public class EventReadWriteTest {
private EventReader<Event> writeReadEvents(Event... events) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
UniversalEventWriter writer = ThreadEventCodec.createEventWriter(bos);
for(Event e: events) {
writer.store(e);
}
writer.close();
byte[] data = bos.toByteArray();
System.out.println("Encoded " + events.length + " events into " + data.length + " bytes");
ByteArrayInputStream bis = new ByteArrayInputStream(data);
EventReader<Event> reader = ThreadEventCodec.createEventReader(bis);
return reader;
}
private EventReader<Event> safeWriteReadEvents(Event... events) throws IOException {
return new MorphingEventReader<Event>(writeReadEvents(events)) {
@Override
protected Event transform(Event event) {
if (event instanceof ErrorEvent) {
throw new RuntimeException(((ErrorEvent) event).exception());
}
return event;
}
};
}
@Test
public void verify_simple_event() throws Exception {
TestDataEvent a = new TestDataEvent();
a.timestamp(1000);
a.tag("A", "1");
a.tag("B", "1");
a.tag("B", "2");
a.tag("C", "0");
a.set("qwerty", 10);
a.set("QWERTY", Long.MAX_VALUE - Integer.MAX_VALUE);
CommonEvent b = (CommonEvent) safeWriteReadEvents(a).iterator().next();
assertThat(b).isNotInstanceOf(ThreadSnapshotEvent.class);
assertThat(b.timestamp()).isEqualTo(1000);
assertThat(b.counters().getValue("qwerty")).isEqualTo(10);
assertThat(b.counters().getValue("QWERTY")).isEqualTo(Long.MAX_VALUE - Integer.MAX_VALUE);
assertThat(b.tags()).containsOnly("A", "B", "C");
assertThat(b.tags().tagsFor("A")).containsOnly("1");
assertThat(b.tags().tagsFor("B")).containsOnly("1", "2");
assertThat(b.tags().tagsFor("C")).containsOnly("0");
assertThat(b).is(eventEquals(a));
}
@Test
public void verify_3_same_events() throws Exception {
TestDataEvent a = new TestDataEvent();
a.timestamp(1000);
a.tag("A", "1");
a.tag("B", "1");
a.tag("B", "2");
a.tag("C", "0");
a.set("qwerty", 10);
a.set("QWERTY", Long.MAX_VALUE - Integer.MAX_VALUE);
EventReader<Event> reader = safeWriteReadEvents(a, a, a);
assertThat((Iterator<Event>)reader).is(EventSeqEqualToCondition.exactlyAs(a, a, a));
}
@Test
public void verify_more_simple_events() throws Exception {
TestDataEvent a1 = new TestDataEvent();
a1.timestamp(1000);
a1.tag("A", "1");
a1.tag("B", "1");
a1.tag("B", "2");
a1.tag("C", "0");
a1.set("qwerty", 10);
a1.set("QWERTY", Long.MAX_VALUE - Integer.MAX_VALUE);
TestDataEvent a2 = new TestDataEvent();
a2.timestamp(2000);
a2.tag("AA", "1");
a2.tag("BB", "1");
a2.tag("BB", "2");
a2.tag("CC", "0");
a2.set("Q", 20);
a2.set("q", Long.MAX_VALUE - Integer.MAX_VALUE);
EventReader<Event> reader = safeWriteReadEvents(a1, a2, a1, a2, a1);
assertThat(reader.next()).is(eventEquals(a1));
assertThat(reader.next()).is(eventEquals(a2));
assertThat(reader.next()).is(eventEquals(a1));
assertThat(reader.next()).is(eventEquals(a2));
assertThat(reader.next()).is(eventEquals(a1));
assertThat(reader.hasNext()).isFalse();
assertThat(reader.hasNext()).isFalse();
}
@Test
public void verify_thread_snapshot() throws IOException {
ThreadEvent a = new ThreadEvent();
a.timestamp(10000);
a.threadId(100);
a.threadName("Abc");
a.stackTrace(genTrace(10));
ThreadSnapshotEvent b = (ThreadSnapshotEvent) safeWriteReadEvents(a).iterator().next();
assertThat(b.timestamp()).isEqualTo(10000);
assertThat(b.threadId()).isEqualTo(100);
assertThat(b.threadName()).isEqualTo("Abc");
assertThat(b.threadState()).isNull();
assertThat(b.counters()).containsExactly("thread.javaId");
assertThat(b.tags().toString()).isEqualTo("[thread.javaName:Abc]");
assertThat(b).is(eventEquals(a));
}
@Test
public void verify_mixed_event_set() throws IOException {
ThreadEvent a1 = new ThreadEvent();
a1.timestamp(10000);
a1.threadId(100);
a1.threadName("Abc");
a1.stackTrace(genTrace(10));
ThreadEvent a2 = new ThreadEvent();
a2.timestamp(20000);
a2.threadId(101);
a2.threadName("xYZ");
a2.stackTrace(genTrace(5));
a2.set("thread.cpu", 1000);
TestDataEvent a3 = new TestDataEvent();
a3.timestamp(1000);
a3.tag("A", "1");
a3.tag("B", "1");
a3.tag("B", "2");
a3.tag("C", "0");
a3.set("qwerty", 10);
a3.set("QWERTY", Long.MAX_VALUE - Integer.MAX_VALUE);
TestDataEvent a4 = new TestDataEvent();
a4.timestamp(2000);
a4.tag("AA", "1");
a4.tag("BB", "1");
a4.tag("BB", "2");
a4.tag("CC", "0");
a4.set("Q", 20);
a4.set("qwerty", Long.MAX_VALUE - Integer.MAX_VALUE);
EventReader<Event> reader = safeWriteReadEvents(a1, a2, a3, a4 , a1, a3, a2, a4);
assertThat((Iterable<Event>)reader).is(exactlyAs(a1, a2, a3, a4 , a1, a3, a2, a4));
}
private StackTraceElement[] genTrace(int n) {
if (n == 0) {
return Thread.currentThread().getStackTrace();
}
else if (n == 1) {
return genTrace(n - 1);
}
else if (n == 2) {
return genTrace(n - 1);
}
else if (n == 3) {
return genTrace(n - 1);
}
else if (n == 4) {
return genTrace(n - 1);
}
else if (n == 5) {
return genTrace(n - 1);
}
else {
return genTrace(n - 1);
}
}
public static class TestDataEvent implements CommonEvent {
long timestamp = 0;
SimpleTagCollection tags = new SimpleTagCollection();
TestCounterCollection counters = new TestCounterCollection();
@Override
public long timestamp() {
return timestamp;
}
@Override
public CounterCollection counters() {
return counters;
}
@Override
public TagCollection tags() {
return tags;
}
public void timestamp(long timestamp) {
this.timestamp = timestamp;
}
public void tag(String key, String tag) {
tags.put(key, tag);
}
public void set(String key, long value) {
counters.set(key, value);
}
}
public static class ThreadEvent extends TestDataEvent implements ThreadSnapshotEvent {
String threadName;
long threadId = -1;
State state = null;
StackFrameList trace;
@Override
public long threadId() {
return threadId;
}
public void threadId(long threadId) {
this.threadId = threadId;
counters.set("thread.javaId", threadId);
}
@Override
public String threadName() {
return threadName;
}
public void threadName(String threadName) {
this.threadName = threadName;
tags.put("thread.javaName", threadName);
}
@Override
public State threadState() {
return state;
}
public void threadState(State state) {
this.state = state;
}
@Override
public StackFrameList stackTrace() {
return trace;
}
public void stackTrace(StackTraceElement[] trace) {
StackFrame[] ftrace = new StackFrame[trace.length];
for(int i = 0; i != trace.length; ++i) {
ftrace[i] = new StackFrame(trace[i]);
}
this.trace = new StackFrameArray(ftrace);
}
}
private static class TestCounterCollection implements CounterCollection {
private Map<String, Long> counters = new TreeMap<String, Long>();
public void set(String key, long value) {
counters.put(key, value);
}
@Override
public Iterator<String> iterator() {
return counters.keySet().iterator();
}
@Override
public long getValue(String key) {
Long n = counters.get(key);
return n == 0 ? Long.MIN_VALUE : n;
}
public TestCounterCollection clone() {
throw new UnsupportedOperationException();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
for(String key: this) {
if (sb.length() > 1) {
sb.append(", ");
}
sb.append(key).append(": ").append(getValue(key));
}
sb.append(']');
return sb.toString();
}
}
}