/* * 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.util.trace; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.log.InvocationInfo; import com.facebook.buck.model.BuildId; import com.facebook.buck.testutil.FakeProjectFilesystem; import com.facebook.buck.timing.FakeClock; import com.facebook.buck.timing.SettableFakeClock; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.trace.BuildTraces.TraceAttributes; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.nio.file.Path; import java.nio.file.attribute.FileTime; import java.util.Optional; import java.util.concurrent.TimeUnit; import org.junit.Test; public class BuildTracesTest { @Test public void testGetTraceAttributesForId() throws IOException { FakeProjectFilesystem projectFilesystem = new FakeProjectFilesystem(new FakeClock(TimeUnit.MILLISECONDS.toNanos(1000L))); projectFilesystem.writeContentsToPath( "[\n" + " {\n" + " \"cat\": \"buck\",\n" + " \"pid\": 0,\n" + " \"ts\": 0,\n" + " \"ph\": \"M\",\n" + " \"args\": {\n" + " \"name\": \"buck\"\n" + " },\n" + " \"name\": \"process_name\",\n" + " \"tid\": 0\n" + " },\n" + " {\n" + " \"cat\": \"buck\",\n" + " \"name\": \"build\",\n" + " \"ph\": \"B\",\n" + " \"pid\": 0,\n" + " \"tid\": 1,\n" + " \"ts\": 5621911884918,\n" + " \"args\": {\n" + " \"command_args\": \"buck\"\n" + " }\n" + " }\n" + "]", projectFilesystem.getBuckPaths().getTraceDir().resolve("build.a.trace")); BuildTraces helper = new BuildTraces(projectFilesystem); TraceAttributes traceAttributes = helper.getTraceAttributesFor("a"); assertEquals( "BuildTraces should be able to extract the command.", Optional.of("buck build buck"), traceAttributes.getCommand()); assertEquals(FileTime.fromMillis(1000L), traceAttributes.getLastModifiedTime()); // We cannot verify the contents of getFormattedDateTime() because they may vary depending on // timezone and locale. assertNotNull(Strings.emptyToNull(traceAttributes.getFormattedDateTime())); } @Test public void testGetTraceAttributesForJsonWithoutName() throws IOException { FakeProjectFilesystem projectFilesystem = new FakeProjectFilesystem(new FakeClock(TimeUnit.MILLISECONDS.toNanos(2000L))); projectFilesystem.writeContentsToPath( "[" + "{" + "\"cat\":\"buck\"," + "\"ph\":\"B\"," + "\"pid\":0," + "\"tid\":1," + "\"ts\":5621911884918," + "\"args\":{\"command_args\":\"buck\"}" + "}" + "]", projectFilesystem.getBuckPaths().getTraceDir().resolve("build.b.trace")); BuildTraces helper = new BuildTraces(projectFilesystem); TraceAttributes traceAttributes = helper.getTraceAttributesFor("b"); assertEquals( "BuildTraces should not be able to extract the command because there is no name " + "attribute.", Optional.empty(), traceAttributes.getCommand()); assertEquals(FileTime.fromMillis(2000L), traceAttributes.getLastModifiedTime()); } @Test public void testGetTraceAttributesForJsonWithoutCommandArgs() throws IOException { FakeProjectFilesystem projectFilesystem = new FakeProjectFilesystem(new FakeClock(TimeUnit.MILLISECONDS.toNanos(2000L))); projectFilesystem.writeContentsToPath( "[" + "{" + "\"cat\":\"buck\"," + "\"ph\":\"B\"," + "\"pid\":0," + "\"tid\":1," + "\"ts\":5621911884918" + "}" + "]", projectFilesystem.getBuckPaths().getTraceDir().resolve("build.c.trace")); BuildTraces helper = new BuildTraces(projectFilesystem); TraceAttributes traceAttributes = helper.getTraceAttributesFor("c"); assertEquals( "BuildTraces should not be able to extract the command because there is no " + "command_args attribute.", Optional.empty(), traceAttributes.getCommand()); assertEquals(FileTime.fromMillis(2000L), traceAttributes.getLastModifiedTime()); } @Test public void testSortByLastModified() throws IOException { SettableFakeClock clock = new SettableFakeClock(0L, 0L); FakeProjectFilesystem projectFilesystem = new FakeProjectFilesystem(clock); clock.setCurrentTimeMillis(1); Path traceDir = projectFilesystem.getBuckPaths().getTraceDir(); projectFilesystem.touch(traceDir.resolve("build.1.trace")); clock.setCurrentTimeMillis(4); projectFilesystem.touch(traceDir.resolve("build.4.trace")); clock.setCurrentTimeMillis(2); projectFilesystem.touch(traceDir.resolve("build.2.trace")); clock.setCurrentTimeMillis(5); projectFilesystem.touch(traceDir.resolve("build.5.trace")); clock.setCurrentTimeMillis(3); projectFilesystem.touch(traceDir.resolve("build.3.trace")); projectFilesystem.touch(traceDir.resolve("build.3b.trace")); BuildTraces helper = new BuildTraces(projectFilesystem); assertEquals( ImmutableList.of( traceDir.resolve("build.5.trace"), traceDir.resolve("build.4.trace"), traceDir.resolve("build.3b.trace"), traceDir.resolve("build.3.trace"), traceDir.resolve("build.2.trace"), traceDir.resolve("build.1.trace")), helper.listTraceFilesByLastModified()); } @Test(expected = HumanReadableException.class) public void testInputsForTracesThrowsWhenEmpty() throws IOException { FakeProjectFilesystem projectFilesystem = new FakeProjectFilesystem(new FakeClock(TimeUnit.MILLISECONDS.toNanos(2000L))); projectFilesystem.mkdirs(projectFilesystem.getBuckPaths().getTraceDir()); BuildTraces helper = new BuildTraces(projectFilesystem); helper.getInputsForTraces("nonexistent"); } @Test(expected = HumanReadableException.class) public void testTraceAttributesThrowsWhenEmpty() throws IOException { FakeProjectFilesystem projectFilesystem = new FakeProjectFilesystem(new FakeClock(TimeUnit.MILLISECONDS.toNanos(2000L))); projectFilesystem.mkdirs(projectFilesystem.getBuckPaths().getTraceDir()); BuildTraces helper = new BuildTraces(projectFilesystem); helper.getTraceAttributesFor("nonexistent"); } @Test public void testFindingTracesInNewPerCommandDirectories() throws IOException { SettableFakeClock clock = new SettableFakeClock(0L, 0L); FakeProjectFilesystem fs = new FakeProjectFilesystem(clock); fs.touch(getNewTraceFilePath(fs, "build", "1", 1)); fs.touch(getNewTraceFilePath(fs, "audit", "4", 4)); fs.touch(getNewTraceFilePath(fs, "query", "2", 2)); fs.touch(getNewTraceFilePath(fs, "targets", "5", 5)); fs.touch(getNewTraceFilePath(fs, "test", "3", 3)); fs.touch(getNewTraceFilePath(fs, "test", "3b", 3)); BuildTraces helper = new BuildTraces(fs); assertEquals( ImmutableList.of( fs.getBuckPaths().getLogDir().resolve("1970-01-01_00h00m05s_targets_5/build.5.trace"), fs.getBuckPaths().getLogDir().resolve("1970-01-01_00h00m04s_audit_4/build.4.trace"), fs.getBuckPaths().getLogDir().resolve("1970-01-01_00h00m03s_test_3b/build.3b.trace"), fs.getBuckPaths().getLogDir().resolve("1970-01-01_00h00m03s_test_3/build.3.trace"), fs.getBuckPaths().getLogDir().resolve("1970-01-01_00h00m02s_query_2/build.2.trace"), fs.getBuckPaths().getLogDir().resolve("1970-01-01_00h00m01s_build_1/build.1.trace")), helper.listTraceFilesByLastModified()); } private static Path getNewTraceFilePath( ProjectFilesystem fs, String commandName, String buildId, int seconds) { InvocationInfo info = InvocationInfo.of( new BuildId(buildId), false, false, commandName, new String[0], new String[0], fs.getBuckPaths().getLogDir()) .withTimestampMillis(TimeUnit.SECONDS.toMillis(seconds)); return info.getLogDirectoryPath().resolve("build." + buildId + ".trace"); } }