/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.scanner.profiling; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.CoreProperties; import org.sonar.api.batch.Initializer; import org.sonar.api.batch.PostJob; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.events.InitializerExecutionHandler; import org.sonar.api.batch.events.InitializersPhaseHandler; import org.sonar.api.batch.events.PostJobExecutionHandler; import org.sonar.api.batch.events.PostJobsPhaseHandler; import org.sonar.api.batch.events.ProjectAnalysisHandler; import org.sonar.api.batch.events.ProjectAnalysisHandler.ProjectAnalysisEvent; import org.sonar.api.batch.events.SensorExecutionHandler; import org.sonar.api.batch.events.SensorExecutionHandler.SensorExecutionEvent; import org.sonar.api.batch.events.SensorsPhaseHandler; import org.sonar.api.batch.events.SensorsPhaseHandler.SensorsPhaseEvent; import org.sonar.api.resources.Project; import org.sonar.api.utils.System2; import org.sonar.scanner.bootstrap.GlobalProperties; import org.sonar.scanner.events.BatchStepEvent; import com.google.common.collect.Maps; public class PhasesSumUpTimeProfilerTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); private MockedSystem clock; private PhasesSumUpTimeProfiler profiler; @Before public void prepare() throws Exception { clock = new MockedSystem(); Map<String, String> props = Maps.newHashMap(); props.put(CoreProperties.WORKING_DIRECTORY, temp.newFolder().getAbsolutePath()); profiler = new PhasesSumUpTimeProfiler(clock, new GlobalProperties(props)); } @Test public void testSimpleProject() throws InterruptedException { final Project project = mockProject("my:project", true); fakeAnalysis(profiler, project); assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(7L); assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(10L); assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(30L); assertThat(profiler.currentModuleProfiling.getProfilingPerBatchStep("Free memory").totalTime()).isEqualTo(9L); } @Test public void testMultimoduleProject() throws InterruptedException { final Project project = mockProject("project root", true); final Project moduleA = mockProject("moduleA", false); final Project moduleB = mockProject("moduleB", false); project.definition().addSubProject(moduleA.definition()); project.definition().addSubProject(moduleA.definition()); fakeAnalysis(profiler, moduleA); fakeAnalysis(profiler, moduleB); fakeAnalysis(profiler, project); assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(7L); assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(10L); assertThat(profiler.currentModuleProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(30L); assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.INIT).getProfilingPerItem(new FakeInitializer()).totalTime()).isEqualTo(21L); assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.SENSOR).getProfilingPerItem(new FakeSensor()).totalTime()).isEqualTo(30L); assertThat(profiler.totalProfiling.getProfilingPerPhase(Phase.POSTJOB).getProfilingPerItem(new FakePostJob()).totalTime()).isEqualTo(90L); } @Test public void testDisplayTimings() { AbstractTimeProfiling profiling = new AbstractTimeProfiling(System2.INSTANCE) { }; profiling.setTotalTime(5); assertThat(profiling.totalTimeAsString()).isEqualTo("5ms"); profiling.setTotalTime(5 * 1000 + 12); assertThat(profiling.totalTimeAsString()).isEqualTo("5s"); profiling.setTotalTime(5 * 60 * 1000 + 12 * 1000); assertThat(profiling.totalTimeAsString()).isEqualTo("5min 12s"); profiling.setTotalTime(5 * 60 * 1000); assertThat(profiling.totalTimeAsString()).isEqualTo("5min"); } private class MockedSystem extends System2 { private long now = 0; @Override public long now() { return now; } public void sleep(long duration) { now += duration; } } private Project mockProject(String name, boolean isRoot) { return new Project(ProjectDefinition.create().setName(name).setKey(name)); } private void fakeAnalysis(PhasesSumUpTimeProfiler profiler, final Project module) { // Start of moduleA profiler.onProjectAnalysis(projectEvent(module, true)); initializerPhase(profiler); sensorPhase(profiler); postJobPhase(profiler); batchStep(profiler); // End of moduleA profiler.onProjectAnalysis(projectEvent(module, false)); } private void batchStep(PhasesSumUpTimeProfiler profiler) { // Start of batch step profiler.onBatchStep(new BatchStepEvent("Free memory", true)); clock.sleep(9); // End of batch step profiler.onBatchStep(new BatchStepEvent("Free memory", false)); } private void initializerPhase(PhasesSumUpTimeProfiler profiler) { Initializer initializer = new FakeInitializer(); // Start of initializer phase profiler.onInitializersPhase(initializersEvent(true)); // Start of an initializer profiler.onInitializerExecution(initializerEvent(initializer, true)); clock.sleep(7); // End of an initializer profiler.onInitializerExecution(initializerEvent(initializer, false)); // End of initializer phase profiler.onInitializersPhase(initializersEvent(false)); } private void sensorPhase(PhasesSumUpTimeProfiler profiler) { Sensor sensor = new FakeSensor(); // Start of sensor phase profiler.onSensorsPhase(sensorsEvent(true)); // Start of a Sensor profiler.onSensorExecution(sensorEvent(sensor, true)); clock.sleep(10); // End of a Sensor profiler.onSensorExecution(sensorEvent(sensor, false)); // End of sensor phase profiler.onSensorsPhase(sensorsEvent(false)); } private void postJobPhase(PhasesSumUpTimeProfiler profiler) { PostJob postJob = new FakePostJob(); // Start of sensor phase profiler.onPostJobsPhase(postJobsEvent(true)); // Start of a Sensor profiler.onPostJobExecution(postJobEvent(postJob, true)); clock.sleep(30); // End of a Sensor profiler.onPostJobExecution(postJobEvent(postJob, false)); // End of sensor phase profiler.onPostJobsPhase(postJobsEvent(false)); } private SensorExecutionEvent sensorEvent(final Sensor sensor, final boolean start) { return new SensorExecutionHandler.SensorExecutionEvent() { @Override public boolean isStart() { return start; } @Override public boolean isEnd() { return !start; } @Override public Sensor getSensor() { return sensor; } }; } private InitializerExecutionHandler.InitializerExecutionEvent initializerEvent(final Initializer initializer, final boolean start) { return new InitializerExecutionHandler.InitializerExecutionEvent() { @Override public boolean isStart() { return start; } @Override public boolean isEnd() { return !start; } @Override public Initializer getInitializer() { return initializer; } }; } private PostJobExecutionHandler.PostJobExecutionEvent postJobEvent(final PostJob postJob, final boolean start) { return new PostJobExecutionHandler.PostJobExecutionEvent() { @Override public boolean isStart() { return start; } @Override public boolean isEnd() { return !start; } @Override public PostJob getPostJob() { return postJob; } }; } private SensorsPhaseEvent sensorsEvent(final boolean start) { return new SensorsPhaseHandler.SensorsPhaseEvent() { @Override public boolean isStart() { return start; } @Override public boolean isEnd() { return !start; } @Override public List<Sensor> getSensors() { return null; } }; } private InitializersPhaseHandler.InitializersPhaseEvent initializersEvent(final boolean start) { return new InitializersPhaseHandler.InitializersPhaseEvent() { @Override public boolean isStart() { return start; } @Override public boolean isEnd() { return !start; } @Override public List<Initializer> getInitializers() { return null; } }; } private PostJobsPhaseHandler.PostJobsPhaseEvent postJobsEvent(final boolean start) { return new PostJobsPhaseHandler.PostJobsPhaseEvent() { @Override public boolean isStart() { return start; } @Override public boolean isEnd() { return !start; } @Override public List<PostJob> getPostJobs() { return null; } }; } private ProjectAnalysisEvent projectEvent(final Project project, final boolean start) { return new ProjectAnalysisHandler.ProjectAnalysisEvent() { @Override public boolean isStart() { return start; } @Override public boolean isEnd() { return !start; } @Override public Project getProject() { return project; } }; } public class FakeSensor implements Sensor { @Override public void analyse(Project project, SensorContext context) { } @Override public boolean shouldExecuteOnProject(Project project) { return true; } } public class FakeInitializer extends Initializer { @Override public void execute(Project project) { } @Override public boolean shouldExecuteOnProject(Project project) { return true; } } public class FakePostJob implements PostJob { @Override public void executeOn(Project project, SensorContext context) { } } }