Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,7 @@ private void checkEnterQueue(Queue.Item i) {
null,
null);
executorService.submit(() -> {
QueueItemMetricsListener.notifyStarted(m);
QueueItemMetricsListener.notifyQueued(m);
Copy link
Member

@dwnusbaum dwnusbaum Nov 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has the potential to be a breaking change, but from a quick GitHub search it looks like there aren't any open source plugins using QueueItemMetricsListener in the first place, so the risk seems limited.

});
return new ItemTotals(id);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,48 +25,89 @@
package jenkins.metrics.impl;

import hudson.ExtensionList;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import hudson.model.queue.QueueTaskFuture;
import jenkins.metrics.api.Metrics;
import jenkins.metrics.api.QueueItemMetricsEvent;
import jenkins.metrics.api.QueueItemMetricsListener;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.SleepBuilder;
import org.jvnet.hudson.test.TestExtension;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.emptyCollectionOf;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;

public class JenkinsMetricProviderImplTest {
@Rule
public JenkinsRule j = new JenkinsRule();

@ClassRule
public static BuildWatcher w = new BuildWatcher();

@Test
public void given__a_job_which_is_cancelled() throws Exception {
MyListener listener = ExtensionList.lookup(QueueItemMetricsListener.class).get(MyListener.class);
assertThat(listener, notNullValue());

FreeStyleProject job = j.createProject(FreeStyleProject.class);

QueueTaskFuture<FreeStyleBuild> taskFuture = job.scheduleBuild2(10);
taskFuture.cancel(true);

try{
j.waitForCompletion(taskFuture.waitForStart());
}catch (Throwable t){
//CancellationException expected
}

//wait for completion
while (!listener.state.toString().contains("C")){
Thread.sleep(10);
}

assertThat(listener.state.toString(), is("QC"));

}

@Test
public void given__a_job__when__built__then__events_and_metrics_include_build() throws Exception {
j.jenkins.setQuietPeriod(0);
MyListener listener = ExtensionList.lookup(QueueItemMetricsListener.class).get(MyListener.class);
assertThat(listener, notNullValue());

FreeStyleProject p = j.createProject(FreeStyleProject.class);
p.setQuietPeriod(0);
p.getBuildersList().add(new SleepBuilder(3000));
assertThat(Metrics.metricRegistry().getTimers().get("jenkins.job.building.duration").getCount(), is(0L));
assertThat(listener.getEvents(), is(emptyCollectionOf(QueueItemMetricsEvent.class)));
j.assertBuildStatusSuccess(p.scheduleBuild2(2));

//wait for completion
while (!listener.state.toString().contains("F")){
Thread.sleep(10);
}
assertThat(listener.state.toString(), is("QSF"));

assertThat(Metrics.metricRegistry().getTimers().get("jenkins.job.building.duration").getCount(), is(1L));
assertThat(Metrics.metricRegistry().getTimers().get("jenkins.job.building.duration").getSnapshot().getMean(),
allOf(greaterThan(TimeUnit.MILLISECONDS.toNanos(2500)*1.0),
lessThan(TimeUnit.MILLISECONDS.toNanos(3500) * 1.0)));
lessThan(TimeUnit.MILLISECONDS.toNanos(3800) * 1.0)));
List<QueueItemMetricsEvent> events = listener.getEvents();
assertThat(events.size(), is(3));
Collections.sort(events, QueueItemMetricsEvent::compareEventSequence);
Expand All @@ -89,16 +130,18 @@ public void given__a_job__when__built__then__events_and_metrics_include_build()
assertThat(events.get(2).getExecutable(), is(Optional.of(p.getBuildByNumber(1))));
assertThat(events.get(2).getQueuingTotalMillis().orElse(null), greaterThan(1500L));
assertThat(events.get(2).getQueuingWaitingMillis().orElse(null), greaterThan(1500L));
assertThat(events.get(2).getExecutingMillis().orElse(null), allOf(greaterThan(2500L), lessThan(3500L)));
assertThat(events.get(2).getExecutingMillis().orElse(null), allOf(greaterThan(2500L), lessThan(3800L)));
assertThat(events.get(2).getExecutorCount().orElse(null), is(1));
}

@TestExtension
public static class MyListener extends QueueItemMetricsListener {
private final List<QueueItemMetricsEvent> events = new ArrayList<>();
public StringBuilder state = new StringBuilder();

@Override
public void onQueued(QueueItemMetricsEvent event) {
state.append("Q");
synchronized (events) {
events.add(event);
}
Expand All @@ -107,20 +150,23 @@ public void onQueued(QueueItemMetricsEvent event) {

@Override
public void onCancelled(QueueItemMetricsEvent event) {
state.append("C");
synchronized (events) {
events.add(event);
}
}

@Override
public void onStarted(QueueItemMetricsEvent event) {
state.append("S");
synchronized (events) {
events.add(event);
}
}

@Override
public void onFinished(QueueItemMetricsEvent event) {
state.append("F");
synchronized (events) {
events.add(event);
}
Expand Down