Skip to content

Commit

Permalink
Add LoggingChecksPublisher and tests for status checks (#59)
Browse files Browse the repository at this point in the history
* Add LoggingChecksPublisher and tests for status checks

* Add documents.

* Checks for empty checks name

* Add constructors for logging publisher to simplify usage
  • Loading branch information
XiongKezhi authored Dec 23, 2020
1 parent 6182093 commit 6d41c27
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package io.jenkins.plugins.checks.status;

import hudson.model.Job;
import hudson.model.Run;
import io.jenkins.plugins.checks.util.LoggingChecksPublisher;
import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerTest;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;

import java.io.IOException;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests that the {@link BuildStatusChecksPublisher} listens to the status of a {@link Run} and publishes status
* accordingly.
*/
public class BuildStatusChecksPublisherITest extends IntegrationTestWithJenkinsPerTest {
private static final String STATUS_TEMPLATE = "Published Checks (name: %s, status: %s, conclusion %s)%n";

/**
* Provide a {@link io.jenkins.plugins.checks.util.LoggingChecksPublisher} to log details.
*/
@TestExtension
public static final LoggingChecksPublisher.Factory PUBLISHER_FACTORY =
new LoggingChecksPublisher.Factory(details -> String.format(STATUS_TEMPLATE,
details.getName().orElseThrow(() -> new IllegalStateException("Empty check name")),
details.getStatus(), details.getConclusion()));

/**
* Provide inject an implementation of {@link AbstractStatusChecksProperties} to control the checks.
*/
@TestExtension
public static final ChecksProperties PROPERTIES = new ChecksProperties();

/**
* Tests when the implementation of {@link AbstractStatusChecksProperties} is not applicable,
* a status checks should not be published.
*
* @throws IOException if failed getting log from {@link Run}
*/
@Test
public void shouldNotPublishStatusWhenNotApplicable() throws IOException {
PROPERTIES.setApplicable(false);

assertThat(JenkinsRule.getLog(buildSuccessfully(createFreeStyleProject())))
.doesNotContain(String.format(STATUS_TEMPLATE, "Test Status", "IN_PROGRESS", "NONE"))
.doesNotContain(String.format(STATUS_TEMPLATE, "Test Status", "COMPLETED", "SUCCESS"));
}

/**
* Tests when status checks is skipped, a status checks should not be published.
*
* @throws IOException if failed getting log from {@link Run}
*/
@Test
public void shouldNotPublishStatusWhenSkipped() throws IOException {
PROPERTIES.setApplicable(true);
PROPERTIES.setSkipped(true);
PROPERTIES.setName("Test Status");

assertThat(JenkinsRule.getLog(buildSuccessfully(createFreeStyleProject())))
.doesNotContain(String.format(STATUS_TEMPLATE, "Test Status", "IN_PROGRESS", "NONE"))
.doesNotContain(String.format(STATUS_TEMPLATE, "Test Status", "COMPLETED", "SUCCESS"));
}

/**
* Tests when an implementation of {@link AbstractStatusChecksProperties} is applicable and not skipped,
* a status checks using the specified name should be published.
*
* @throws IOException if failed getting log from {@link Run}
*/
@Test
public void shouldPublishStatusWithProperties() throws IOException {
PROPERTIES.setApplicable(true);
PROPERTIES.setSkipped(false);
PROPERTIES.setName("Test Status");

Run<?, ?> run = buildSuccessfully(createFreeStyleProject());
assertThat(JenkinsRule.getLog(run))
.contains(String.format(STATUS_TEMPLATE, "Test Status", "IN_PROGRESS", "NONE"))
.contains(String.format(STATUS_TEMPLATE, "Test Status", "COMPLETED", "SUCCESS"));
}

static class ChecksProperties extends AbstractStatusChecksProperties {
private boolean applicable;
private boolean skipped;
private String name;

public void setApplicable(final boolean applicable) {
this.applicable = applicable;
}

public void setSkipped(final boolean skipped) {
this.skipped = skipped;
}

public void setName(final String name) {
this.name = name;
}

@Override
public boolean isApplicable(final Job<?, ?> job) {
return applicable;
}

@Override
public String getName(final Job<?, ?> job) {
return name;
}

@Override
public boolean isSkipped(final Job<?, ?> job) {
return skipped;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package io.jenkins.plugins.checks.util;

import hudson.model.Job;
import hudson.model.Run;
import hudson.model.TaskListener;
import io.jenkins.plugins.checks.api.ChecksDetails;
import io.jenkins.plugins.checks.api.ChecksPublisher;
import io.jenkins.plugins.checks.api.ChecksPublisherFactory;

import java.util.Optional;

/**
* Implementation of {@link ChecksPublisher} for use in testing, that logs the checks details in user specified format.
*
* For example:
*
* <pre>
* public class ChecksPublishingTest extends IntegrationTestWithJenkinsPerTest {
*
* &#64;TestExtension
* public static final LoggingChecksPublisher.Factory PUBLISHER_FACTORY = new LoggingChecksPublisher.Factory();
*
* &#64;Test
* public void shouldLogChecks() {
* // ...Set up the formatter...
* PUBLISHER_FACTORY.setFormatter(details -> String.format(STATUS_TEMPLATE,
* details.getName().orElseThrow(() -> new IllegalStateException("Empty check name")),
* details.getStatus(), details.getConclusion()));
*
* // ...Run a test job...
* Run<?, ?> run = buildSuccessfully(createFreeStyleProject());
*
* // ...Inspect logs...
* assertThat(JenkinsRule.getLog(run))
* .contains("...")
* .doesNotContain("...");
* }
* }
* </pre>
*
* An example of this can be found in {@link io.jenkins.plugins.checks.status.BuildStatusChecksPublisherITest}
*/
public class LoggingChecksPublisher extends ChecksPublisher {
private Formatter formatter = ChecksDetails::toString;
private TaskListener listener = TaskListener.NULL;

/**
* Logs the {@code details} using the {@link TaskListener}.
*
* @param details
* checks details that will be logged
*/
@Override
public void publish(final ChecksDetails details) {
listener.getLogger().print(formatter.format(details));
}

/**
* Implementation of {@link ChecksPublisherFactory} that returns a {@link LoggingChecksPublisher}.
*/
public static class Factory extends ChecksPublisherFactory {
private final LoggingChecksPublisher publisher = new LoggingChecksPublisher();

/**
* Constructs without adding formatter.
*/
public Factory() {
}

/**
* Constructs with the formatter which will be used to format {@link ChecksDetails} when publishing.
*
* @param formatter
* formatter for {@link ChecksDetails}
*/
public Factory(final Formatter formatter) {
publisher.formatter = formatter;
}

public void setFormatter(final Formatter formatter) {
publisher.formatter = formatter;
}

@Override
protected Optional<ChecksPublisher> createPublisher(final Run<?, ?> run, final TaskListener listener) {
publisher.listener = listener;
return Optional.of(publisher);
}

@Override
protected Optional<ChecksPublisher> createPublisher(final Job<?, ?> job, final TaskListener listener) {
publisher.listener = listener;
return Optional.of(publisher);
}
}

/**
* Defines how to format a {@link ChecksDetails} to {@link String}.
*/
@FunctionalInterface
public interface Formatter {
/**
* Formats the {@code details}.
*
* @param details
* details to format.
* @return formatted string
*/
String format(ChecksDetails details);
}
}

0 comments on commit 6d41c27

Please sign in to comment.