/* * Copyright 2011 Google Inc. * * 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.google.gwt.dev.util.log.speedtracer; import com.google.gwt.dev.json.JsonArray; import com.google.gwt.dev.json.JsonException; import com.google.gwt.dev.json.JsonObject; import com.google.gwt.dev.shell.DevModeSession; import com.google.gwt.dev.shell.DevModeSessionTestUtil; import com.google.gwt.dev.util.log.dashboard.SpeedTracerLoggerTestMockNotifier; import com.google.gwt.dev.util.log.dashboard.SpeedTracerLoggerTestMockNotifier.DevModeEvent; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.EventType; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Format; import junit.framework.TestCase; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.util.LinkedList; import java.util.Properties; /** * Tests the SpeedTracerLogger class. */ public class SpeedTracerLoggerTest extends TestCase { private static class DummyEventType implements EventType { private final String color; private final String name; public DummyEventType(String name, String color) { this.name = name; this.color = color; } @Override public String getColor() { return color; } @Override public String getName() { return name; } } private class TestLoggerThreadedThread extends Thread { private final EventType event; private final SpeedTracerLogger logger; public TestLoggerThreadedThread(EventType event, SpeedTracerLogger logger) { super(); this.event = event; this.logger = logger; } @Override public void run() { for (int i = 0; i < MAX_EVENT_LOGS; i++) { Event e = logger.startImpl(null, event); logger.endImpl(e); } } } private static final EventType dummyOne = new DummyEventType("Larry", "Red"); private static final EventType dummyThree = new DummyEventType("Moe", "Blue"); private static final EventType dummyTwo = new DummyEventType("Curly", "Green"); private static final int MAX_EVENT_LOGS = 10000; public void testSpeedTracerLogger() throws IOException, JsonException { Writer writer = new StringWriter(); SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.HTML); Event dummyOneEvent = logger.startImpl(null, dummyOne); Event dummyTwoEvent = logger.startImpl(null, dummyTwo); logger.endImpl(dummyTwoEvent); Event dummyThreeEvent = logger.startImpl(null, dummyThree); logger.endImpl(dummyThreeEvent); logger.endImpl(dummyOneEvent); logger.flush(); Reader jsonReader = extractJsonFromWriter(writer); JsonObject parsedObject = JsonObject.parse(jsonReader); assertTrue("dummyOne", compareJsonToEvent(parsedObject, dummyOne)); JsonObject dataObject = parsedObject.get("data").asObject(); assertNotNull(dataObject); JsonArray childArray = parsedObject.get("children").asArray(); assertNotNull(childArray); assertEquals(2, childArray.getLength()); JsonObject child = childArray.get(0).asObject(); assertTrue("dummyTwo", compareJsonToEvent(child, dummyTwo)); child = childArray.get(1).asObject(); assertTrue("dummyThree", compareJsonToEvent(child, dummyThree)); } public void testSpeedTracerLoggerExtraData() throws IOException, JsonException { Writer writer = new StringWriter(); SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.HTML); Event dummyOneEvent = logger.startImpl(null, dummyOne, "extraStart", "valueStart"); logger.addDataImpl("extraMiddle", "valueMiddle"); logger.endImpl(dummyOneEvent, "extraEnd", "valueEnd"); logger.flush(); Reader jsonReader = extractJsonFromWriter(writer); JsonObject parsedObject = JsonObject.parse(jsonReader); assertEquals("valueStart", parsedObject.get("data").asObject().get( "extraStart").asString().getString()); assertEquals( "valueMiddle", parsedObject.get("data").asObject().get("extraMiddle").asString().getString()); assertEquals( "valueEnd", parsedObject.get("data").asObject().get("extraEnd").asString().getString()); } public void testSpeedTracerLoggerMultiple() throws IOException, JsonException { Writer writer = new StringWriter(); SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.HTML); Event dummyOneEvent = logger.startImpl(null, dummyOne); logger.endImpl(dummyOneEvent); Event dummyTwoEvent = logger.startImpl(null, dummyTwo); logger.endImpl(dummyTwoEvent); Event dummyThreeEvent = logger.startImpl(null, dummyThree); logger.endImpl(dummyThreeEvent); logger.flush(); Reader jsonReader = extractJsonFromWriter(writer); JsonObject dummyOneObject = JsonObject.parse(jsonReader).asObject(); JsonObject dummyTwoObject = JsonObject.parse(jsonReader).asObject(); JsonObject dummyThreeObject = JsonObject.parse(jsonReader).asObject(); assertTrue(compareJsonToEvent(dummyOneObject, dummyOne)); assertTrue(compareJsonToEvent(dummyTwoObject, dummyTwo)); assertTrue(compareJsonToEvent(dummyThreeObject, dummyThree)); } public void testSpeedTracerLoggerThreaded() throws InterruptedException, IOException { final int NUM_THREADS = 3; Writer writer = new StringWriter(); SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.HTML); Thread threads[] = new Thread[NUM_THREADS]; threads[0] = new TestLoggerThreadedThread(dummyOne, logger); threads[1] = new TestLoggerThreadedThread(dummyTwo, logger); threads[2] = new TestLoggerThreadedThread(dummyThree, logger); for (int i = 0; i < NUM_THREADS; i++) { threads[i].start(); } for (int i = 0; i < NUM_THREADS; i++) { threads[i].join(); } logger.flush(); BufferedReader jsonReader = extractJsonFromWriter(writer); int tally[] = new int[NUM_THREADS]; for (int i = 0; i < MAX_EVENT_LOGS * NUM_THREADS; i++) { JsonObject result = null; try { result = JsonObject.parse(jsonReader).asObject(); } catch (JsonException ex) { fail("Failed to parse json after " + i + " iterations. " + ex.toString()); } if (compareJsonToEvent(result, dummyOne)) { tally[0]++; } else if (compareJsonToEvent(result, dummyTwo)) { tally[1]++; } else if (compareJsonToEvent(result, dummyThree)) { tally[2]++; } else { fail("Node with typeName " + result.get("typeName").asString().getString() + " doesn't match expected"); } } for (int i = 0; i < NUM_THREADS; i++) { assertEquals("dummy" + i + " has the wrong number of logs.", tally[i], MAX_EVENT_LOGS); } } public void testSpeedTracerLoggerMarkTimeline() throws IOException, JsonException { Writer writer = new StringWriter(); SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.RAW); Event dummyOneEvent = logger.startImpl(null, dummyOne); logger.markTimelineImpl("Test Message"); dummyOneEvent.end(); logger.flush(); // There should be no HTML in here String logString = writer.toString(); BufferedReader jsonReader = new BufferedReader(new StringReader(logString)); JsonObject dummyOneObject = JsonObject.parse(jsonReader).asObject(); assertTrue(compareJsonToEvent(dummyOneObject, dummyOne)); JsonArray children = dummyOneObject.get("children").asArray(); assertEquals(1, children.getLength()); JsonObject markTimelineObject = children.get(0).asObject(); assertEquals(11.0, markTimelineObject.get("type").asNumber().getDecimal(), .001); JsonObject dataObject = markTimelineObject.get("data").asObject(); assertEquals("json=" + logString, "Test Message", dataObject.get("message").asString().getString()); } public void testSpeedTracerLoggerRaw() throws IOException, JsonException { Writer writer = new StringWriter(); SpeedTracerLogger logger = new SpeedTracerLogger(writer, Format.RAW); Event dummyOneEvent = logger.startImpl(null, dummyOne); dummyOneEvent.end(); logger.flush(); // There should be no HTML in here String logString = writer.toString(); assertTrue(logString.trim().startsWith("{")); assertTrue(logString.trim().endsWith("}")); BufferedReader jsonReader = new BufferedReader(new StringReader(logString)); JsonObject dummyOneObject = JsonObject.parse(jsonReader).asObject(); assertTrue(compareJsonToEvent(dummyOneObject, dummyOne)); } private boolean compareJsonToEvent(JsonObject jsonObject, EventType eventType) { String typeName = jsonObject.get("typeName").asString().getString(); String color = jsonObject.get("color").asString().getString(); return typeName.equals(eventType.getName()) && color.equals(eventType.getColor()); } private BufferedReader extractJsonFromWriter(Writer writer) throws IOException { String jsonString = writer.toString(); assertTrue(jsonString.substring(0,5).toLowerCase().startsWith("<html")); BufferedReader jsonReader = new BufferedReader(new StringReader(jsonString)); // Skip ahead to start of JSON while (true) { jsonReader.mark(16 * 1024); String line = jsonReader.readLine(); if (line == null) { fail("Didn't find start of JSON string"); } if (line.startsWith("{")) { jsonReader.reset(); break; } } return jsonReader; } public void testSpeedTracerWhenOnlyDashboardEnabled() { // backup system properties before making changes to them Properties props = (Properties) System.getProperties().clone(); try { // no logging to file! System.clearProperty("gwt.speedtracerlog"); // we don't capture GC events in dashboard, so setting this will allow us // to confirm that they *don't* show up in dashboard notices System.setProperty("gwt.speedtracer.logGcTime", "yes"); // now enable the mock dashboard notifier SpeedTracerLoggerTestMockNotifier notifier = SpeedTracerLoggerTestMockNotifier.enable(); // create "sessions" DevModeSession session1 = DevModeSessionTestUtil.createSession("test1", "test", true); DevModeSession session2 = DevModeSessionTestUtil.createSession("test2", "test", false); // expected values (used in final assertions below) LinkedList<DevModeEvent> expectedEvents = new LinkedList<DevModeEvent>(); LinkedList<DevModeSession> expectedSessions = new LinkedList<DevModeSession>(); Event evt1, evt2; // test events with no session specified evt1 = SpeedTracerLogger.start(DevModeEventType.MODULE_INIT, "k1", "v1", "k2", "v2"); // also test that child events aren't posted (only top-level events) evt2 = SpeedTracerLogger.start(DevModeEventType.CLASS_BYTES_REWRITE); evt2.end(); evt1.end(); // expect only first event expectedEvents.add(new DevModeEvent(evt1)); expectedSessions.add(session1); // event should get "default" session // now with session specified evt1 = SpeedTracerLogger.start(session2, DevModeEventType.JAVA_TO_JS_CALL, "k1", "v1"); // also test that child events aren't posted (only top-level events) evt2 = SpeedTracerLogger.start(DevModeEventType.CREATE_UI); evt2.end(); evt1.end(); // expect only first event expectedEvents.add(new DevModeEvent(evt1)); expectedSessions.add(session2); evt1 = SpeedTracerLogger.start(session1, DevModeEventType.JS_TO_JAVA_CALL, "k1", "v1"); evt1.end(); expectedEvents.add(new DevModeEvent(evt1)); expectedSessions.add(session1); // Finally, assert that the events and corresponding sessions sent to the // notifier are exactly as expected assertEquals("Events posted to dashboard do not match expected events!", expectedEvents, notifier.getEventSequence()); // Collect sessions associated with each event LinkedList<DevModeSession> actualSessions = new LinkedList<DevModeSession>(); for (DevModeEvent event : notifier.getEventSequence()) { actualSessions.add(event.getDevModeSession()); } // and confirm the sessions are correct assertEquals("Events posted to dashboard are associated with incorrect sessions!", expectedSessions, actualSessions); } finally { // restore system properties System.setProperties(props); } } }