/* * Copyright 2013-present Facebook, 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.facebook.buck.event; import static org.hamcrest.MatcherAssert.assertThat; import com.facebook.buck.artifact_cache.CacheResult; import com.facebook.buck.model.BuildId; import com.facebook.buck.model.BuildTargetFactory; import com.facebook.buck.parser.ParseEvent; import com.facebook.buck.rules.BuildEvent; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleDurationTracker; import com.facebook.buck.rules.BuildRuleEvent; import com.facebook.buck.rules.BuildRuleKeys; import com.facebook.buck.rules.BuildRuleStatus; import com.facebook.buck.rules.BuildRuleSuccessType; import com.facebook.buck.rules.FakeBuildRule; import com.facebook.buck.rules.IndividualTestEvent; import com.facebook.buck.rules.RuleKey; import com.facebook.buck.rules.TestRunEvent; import com.facebook.buck.test.FakeTestResults; import com.facebook.buck.test.selectors.TestSelectorList; import com.facebook.buck.testutil.JsonMatcher; import com.facebook.buck.timing.Clock; import com.facebook.buck.timing.DefaultClock; import com.facebook.buck.util.ObjectMappers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.util.Optional; import java.util.Random; import org.junit.Before; import org.junit.Test; /** * While it may seem that the main purpose of this test is to annoy anyone making changes to the * Events under test, the purpose of this test is to make sure we're not making accidental changes * to the serialized JSON representation of this events, as that JSON is the public API exposed via * the websocket to the IntelliJ plugin and anyone else who might be listening. */ public class EventSerializationTest { private long timestamp; private long nanoTime; private long threadUserNanoTime; private long threadId; private BuildId buildId; private BuildRuleDurationTracker durationTracker; @Before public void setUp() { Clock clock = new DefaultClock(); timestamp = clock.currentTimeMillis(); nanoTime = clock.nanoTime(); // Not using real value as not all JVMs will support thread user time. threadUserNanoTime = new Random().nextLong(); threadId = 0; buildId = new BuildId("Test"); durationTracker = new BuildRuleDurationTracker(); EventKey.setSequenceValueForTest(4242L); } @Test public void testConsoleEvent() throws IOException { ConsoleEvent event = ConsoleEvent.severe("Something happened"); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"type\":\"ConsoleEvent\",\"eventKey\":{\"value\":4242}," + "\"level\": {\"name\":\"SEVERE\",\"resourceBundleName\":" + "\"sun.util.logging.resources.logging\",\"localizedName\":\"SEVERE\"}," + " \"message\":\"Something happened\"}", message); } @Test public void testProjectGenerationEventFinished() throws IOException { ProjectGenerationEvent.Finished event = ProjectGenerationEvent.finished(); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"type\":\"ProjectGenerationFinished\"," + "\"eventKey\":{\"value\":4242}}", message); } @Test public void testProjectGenerationEventStarted() throws IOException { ProjectGenerationEvent.Started event = ProjectGenerationEvent.started(); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"type\":\"ProjectGenerationStarted\"," + "\"eventKey\":{\"value\":4242}}", message); } @Test public void testParseEventStarted() throws IOException { ParseEvent.Started event = ParseEvent.started(ImmutableList.of()); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"buildTargets\":[],\"type\":\"ParseStarted\"," + "\"eventKey\":{\"value\":4242}}", message); } @Test public void testParseEventFinished() throws IOException { ParseEvent.Started started = ParseEvent.started(ImmutableList.of(BuildTargetFactory.newInstance("//base:short#flv"))); ParseEvent.Finished event = ParseEvent.finished(started, 10, Optional.empty()); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s," + "\"buildTargets\":[{\"baseName\":\"//base\"," + "\"shortName\":\"short\",\"flavor\":\"flv\"}],\"type\":\"ParseFinished\"," + "\"eventKey\":{\"value\":4242}, \"processedBytes\": 10}", message); } @Test public void testBuildEventStarted() throws IOException { BuildEvent.Started event = BuildEvent.started(ImmutableSet.of("//base:short")); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"eventKey\":{\"value\":4242}," + "\"buildArgs\":[\"//base:short\"]," + "\"type\":\"BuildStarted\"}", message); } @Test public void testBuildEventFinished() throws IOException { BuildEvent.Finished event = BuildEvent.finished(BuildEvent.started(ImmutableSet.of("//base:short")), 0); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"eventKey\":{\"value\":4242}," + "\"buildArgs\":[\"//base:short\"], \"exitCode\":0,\"type\":\"BuildFinished\"}", message); } @Test public void testBuildRuleEventStarted() throws IOException { BuildRule rule = new FakeBuildRule("//fake:rule"); BuildRuleEvent.Started event = BuildRuleEvent.started(rule, durationTracker); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"buildRule\":{\"type\":\"fake_build_rule\",\"name\":\"//fake:rule\"}," + "\"type\":\"BuildRuleStarted\",\"duration\":{\"wallMillisDuration\":0,\"nanoDuration\":0," + "\"threadUserNanoDuration\":0},\"ruleRunningAfterThisEvent\":true," + "\"eventKey\":{\"value\":1024186770}}", message); } @Test public void testBuildRuleEventFinished() throws IOException { BuildRule rule = new FakeBuildRule("//fake:rule"); BuildRuleEvent.Started started = BuildRuleEvent.started(rule, durationTracker); started.configure(timestamp - 11, nanoTime - 12, threadUserNanoTime - 13, threadId, buildId); BuildRuleEvent.Finished event = BuildRuleEvent.finished( started, BuildRuleKeys.of(new RuleKey("aaaa")), BuildRuleStatus.SUCCESS, CacheResult.miss(), Optional.empty(), Optional.of(BuildRuleSuccessType.BUILT_LOCALLY), Optional.empty(), Optional.empty(), Optional.empty()); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"status\":\"SUCCESS\",\"cacheResult\":{\"type\":\"MISS\"}," + "\"buildRule\":{\"type\":" + "\"fake_build_rule\",\"name\":\"//fake:rule\"}," + "\"type\":\"BuildRuleFinished\"," + "\"duration\":{" + "\"wallMillisDuration\":11,\"nanoDuration\":12,\"threadUserNanoDuration\":13}," + "\"ruleRunningAfterThisEvent\":false," + "\"eventKey\":{\"value\":1024186770}," + "\"ruleKeys\":{\"ruleKey\":{\"hashCode\":\"aaaa\"}}}", message); } @Test public void testTestRunEventStarted() throws IOException { TestRunEvent.Started event = TestRunEvent.started(true, TestSelectorList.empty(), false, ImmutableSet.of()); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"runAllTests\":true," + "\"eventKey\":{\"value\":256329280}," + "\"targetNames\":[],\"type\":\"RunStarted\"}", message); } @Test public void testTestRunEventFinished() throws IOException { TestRunEvent.Finished event = TestRunEvent.finished( ImmutableSet.of("target"), ImmutableList.of(FakeTestResults.newFailedInstance("Test1"))); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s," + "\"results\":[{\"testCases\":[{\"testCaseName\":\"Test1\",\"testResults\":[{" + "\"testCaseName\":\"Test1\",\"type\":\"FAILURE\",\"time\":0}]," + "\"failureCount\":1,\"skippedCount\":0,\"totalTime\":0," + "\"success\":false}]," + "\"failureCount\":1,\"contacts\":[],\"labels\":[]," + "\"dependenciesPassTheirTests\":true,\"sequenceNumber\":0,\"totalNumberOfTests\":0," + "\"buildTarget\":{\"shortName\":\"baz\",\"baseName\":\"//foo/bar\"," + "\"flavor\":\"\"}," + "\"success\":false}],\"type\":\"RunComplete\", \"eventKey\":" + "{\"value\":-624576559}}", message); } @Test public void testIndividualTestEventStarted() throws IOException { IndividualTestEvent.Started event = IndividualTestEvent.started(ImmutableList.of("")); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"type\":\"AwaitingResults\",\"eventKey\":{\"value\":-594614447}}", message); } @Test public void testIndividualTestEventFinished() throws IOException { IndividualTestEvent.Finished event = IndividualTestEvent.finished( ImmutableList.of(), FakeTestResults.newFailedInstance("Test1")); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s,\"eventKey\":{\"value\":-594614477}," + "\"results\":{\"testCases\":[{\"testCaseName\":\"Test1\",\"testResults\":[{" + "\"testCaseName\":\"Test1\",\"type\":\"FAILURE\",\"time\":0}]," + "\"failureCount\":1,\"skippedCount\":0,\"totalTime\":0," + "\"success\":false}]," + "\"failureCount\":1,\"contacts\":[],\"labels\":[]," + "\"dependenciesPassTheirTests\":true,\"sequenceNumber\":0,\"totalNumberOfTests\":0," + "\"buildTarget\":{\"shortName\":\"baz\",\"baseName\":\"//foo/bar\"," + "\"flavor\":\"\"}," + "\"success\":false},\"type\":\"ResultsAvailable\"}", message); } @Test public void testSimplePerfEvent() throws IOException { SimplePerfEvent.Started event = SimplePerfEvent.started(PerfEventId.of("PerfId"), "value", "Some value"); event.configure(timestamp, nanoTime, threadUserNanoTime, threadId, buildId); String message = ObjectMappers.WRITER.writeValueAsString(event); assertJsonEquals( "{%s," + "\"eventKey\":{\"value\":4242},\"eventId\":\"PerfId\",\"eventType\":\"STARTED\"," + "\"eventInfo\":{\"value\":\"Some value\"},\"type\":\"PerfEventPerfIdStarted\"}", message); } private void assertJsonEquals(String expected, String actual) throws IOException { String commonHeader = String.format( "\"timestamp\":%d,\"nanoTime\":%d,\"threadUserNanoTime\":%d," + "\"threadId\":%d,\"buildId\":\"%s\"", timestamp, nanoTime, threadUserNanoTime, threadId, buildId); assertThat(actual, new JsonMatcher(String.format(expected, commonHeader))); } }