/* * Copyright 2015-2016 the original author or authors. * * 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 org.glowroot.common.model; import java.io.IOException; import java.util.List; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; import com.google.common.io.CharStreams; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class MutableProfileTest { @Test public void testSingleStackTrace() throws IOException { // given MutableProfile profile = new MutableProfile(); List<StackTraceElement> stackTraceElements = Lists.newArrayList(); stackTraceElements.add(new StackTraceElement("aa.bb.cc.Def", "ghi", "Def.java", 123)); stackTraceElements.add(new StackTraceElement("aa.bb.cc.Def", "ghi", "Def.java", 456)); stackTraceElements.add(new StackTraceElement("xx.yy.zz.Main", "main", "Main.java", 789)); // when profile.merge(stackTraceElements, Thread.State.RUNNABLE); // then assertThat(profile.toJson()).isEqualTo(("{" + " \"unfilteredSampleCount\": 1," + " \"rootNodes\": [" + " {" + " \"stackTraceElement\": \"xx.yy.zz.Main.main(Main.java:789)\"," + " \"sampleCount\": 1," + " \"childNodes\": [" + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:456)\"," + " \"sampleCount\": 1," + " \"childNodes\": [" + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:123)\"," + " \"leafThreadState\": \"RUNNABLE\"," + " \"sampleCount\": 1" + " }" + " ]" + " }" + " ]" + " }" + " ]" + "}").replace(" ", "")); } @Test public void testMerging() throws IOException { // given MutableProfile profile = new MutableProfile(); List<StackTraceElement> stackTraceElements = Lists.newArrayList(); stackTraceElements.add(new StackTraceElement("aa.bb.cc.Def", "ghi", "Def.java", 123)); stackTraceElements.add(new StackTraceElement("aa.bb.cc.Def", "ghi", "Def.java", 456)); stackTraceElements.add(new StackTraceElement("xx.yy.zz.Main", "main", "Main.java", 789)); // when profile.merge(stackTraceElements, Thread.State.RUNNABLE); profile.merge(stackTraceElements, Thread.State.BLOCKED); // then assertThat(profile.toJson()).isEqualTo(("{" + " \"unfilteredSampleCount\": 2," + " \"rootNodes\": [" + " {" + " \"stackTraceElement\": \"xx.yy.zz.Main.main(Main.java:789)\"," + " \"sampleCount\": 2," + " \"childNodes\": [" + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:456)\"," + " \"sampleCount\": 2," + " \"childNodes\": [" + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:123)\"," + " \"leafThreadState\": \"RUNNABLE\"," + " \"sampleCount\": 1" + " }," + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:123)\"," + " \"leafThreadState\": \"BLOCKED\"," + " \"sampleCount\": 1" + " }" + " ]" + " }" + " ]" + " }" + " ]" + "}").replace(" ", "")); } @Test public void testMultipleRootNodes() throws IOException { // given MutableProfile profile = new MutableProfile(); List<StackTraceElement> stackTraceElements1 = Lists.newArrayList(); stackTraceElements1.add(new StackTraceElement("aa.bb.cc.Def", "ghi", "Def.java", 123)); stackTraceElements1.add(new StackTraceElement("aa.bb.cc.Def", "ghi", "Def.java", 456)); stackTraceElements1.add(new StackTraceElement("xx.yy.zz.Main", "main", "Main.java", 789)); List<StackTraceElement> stackTraceElements2 = Lists.newArrayList(); stackTraceElements2.add(new StackTraceElement("aa.bb.cc.Def", "ghi", "Def.java", 123)); stackTraceElements2.add(new StackTraceElement("aa.bb.cc.Def", "ghi", "Def.java", 456)); stackTraceElements2.add(new StackTraceElement("xx.yy.zz.Main", "main2", "Main.java", 789)); // when profile.merge(stackTraceElements1, Thread.State.RUNNABLE); profile.merge(stackTraceElements2, Thread.State.RUNNABLE); // then assertThat(profile.toJson()).isEqualTo(("{" + " \"unfilteredSampleCount\": 2," + " \"rootNodes\": [" + " {" + " \"stackTraceElement\": \"xx.yy.zz.Main.main(Main.java:789)\"," + " \"sampleCount\": 1," + " \"childNodes\": [" + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:456)\"," + " \"sampleCount\": 1," + " \"childNodes\": [" + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:123)\"," + " \"leafThreadState\": \"RUNNABLE\"," + " \"sampleCount\": 1" + " }" + " ]" + " }" + " ]" + " }," + " {" + " \"stackTraceElement\": \"xx.yy.zz.Main.main2(Main.java:789)\"," + " \"sampleCount\": 1," + " \"childNodes\": [" + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:456)\"," + " \"sampleCount\": 1," + " \"childNodes\": [" + " {" + " \"stackTraceElement\": \"aa.bb.cc.Def.ghi(Def.java:123)\"," + " \"leafThreadState\": \"RUNNABLE\"," + " \"sampleCount\": 1" + " }" + " ]" + " }" + " ]" + " }" + " ]" + "}").replace(" ", "")); } // this is helpful when building tests @SuppressWarnings("unused") private static void prettyPrint(String json) throws IOException { ObjectMapper mapper = new ObjectMapper(); JsonNode node = mapper.readTree(json); CustomPrettyPrinter prettyPrinter = new CustomPrettyPrinter(); prettyPrinter.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE); StringBuilder sb = new StringBuilder(); JsonGenerator jg = mapper.getFactory().createGenerator(CharStreams.asWriter(sb)) .setPrettyPrinter(prettyPrinter); jg.writeTree(node); jg.close(); System.out.println(sb.toString().replace("\"", "\\\"").replace(DefaultIndenter.SYS_LF, "\"" + DefaultIndenter.SYS_LF + " + \"")); } @SuppressWarnings("serial") private static class CustomPrettyPrinter extends DefaultPrettyPrinter { @Override public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException { jg.writeRaw(": "); } } }