/* * 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.sonarsource.sonarqube.perf; import com.google.common.base.Joiner; import org.hamcrest.CustomMatcher; import org.junit.rules.ErrorCollector; import org.junit.runner.Description; import org.junit.runners.model.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public abstract class PerfRule extends ErrorCollector { private final int runCount; private final List<List<Long>> recordedResults = new ArrayList<List<Long>>(); private int currentRun; private String testName; public PerfRule(int runCount) { this.runCount = runCount; } @Override public Statement apply(final Statement base, Description description) { this.testName = description.getMethodName(); return new Statement() { @Override public void evaluate() throws Throwable { for (currentRun = 1; currentRun <= runCount; currentRun++) { recordedResults.add(new ArrayList<Long>()); beforeEachRun(); base.evaluate(); } verify(); } }; } protected abstract void beforeEachRun(); public void assertDurationAround(long duration, long expectedDuration) { currentResults().add(duration); if (isLastRun()) { long meanDuration = computeAverageDurationOfCurrentStep(); double variation = 100.0 * (0.0 + meanDuration - expectedDuration) / expectedDuration; checkThat(String.format("Expected %d ms in average, got %d ms [%s]", expectedDuration, meanDuration, Joiner.on(",").join(getAllResultsOfCurrentStep())), Math.abs(variation), new CustomMatcher<Double>( "a value less than " + PerfTestCase.ACCEPTED_DURATION_VARIATION_IN_PERCENTS) { @Override public boolean matches(Object item) { return ((item instanceof Double) && ((Double) item).compareTo(PerfTestCase.ACCEPTED_DURATION_VARIATION_IN_PERCENTS) < 0); } }); } } private Long[] getAllResultsOfCurrentStep() { Long[] result = new Long[runCount]; for (int i = 0; i < runCount; i++) { result[i] = recordedResults.get(i).get(currentResults().size() - 1); } return result; } private long computeAverageDurationOfCurrentStep() { Long[] result = getAllResultsOfCurrentStep(); // Compute a truncated mean by ignoring greater value Arrays.sort(result); long meanDuration = 0; for (int i = 0; i < (runCount - 1); i++) { meanDuration += result[i]; } meanDuration /= (runCount - 1); return meanDuration; } private List<Long> currentResults() { return recordedResults.get(currentRun - 1); } public void assertDurationLessThan(long duration, final long maxDuration) { currentResults().add(duration); if (isLastRun()) { long meanDuration = computeAverageDurationOfCurrentStep(); checkThat(String.format("Expected less than %d ms in average, got %d ms [%s]", maxDuration, meanDuration, Joiner.on(",").join(getAllResultsOfCurrentStep())), meanDuration, new CustomMatcher<Long>("a value less than " + maxDuration) { @Override public boolean matches(Object item) { return ((item instanceof Long) && ((Long) item).compareTo(maxDuration) < 0); } }); } } private boolean isLastRun() { return currentRun == runCount; } }