Skip to content

Commit

Permalink
implemented "send to culprit" feature. refs jenkinsci#1
Browse files Browse the repository at this point in the history
  • Loading branch information
KengoTODA committed Jan 13, 2013
1 parent a999688 commit d4d2473
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package jp.skypencil.jenkins.regression;

import static com.google.common.collect.Iterables.transform;
import hudson.Extension;
import hudson.Launcher;
import hudson.Util;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.User;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Notifier;
Expand All @@ -24,6 +26,7 @@
import javax.mail.Address;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
Expand All @@ -42,12 +45,29 @@
* @author eller86 (Kengo TODA)
*/
public final class RegressionReportNotifier extends Notifier {
private static final int MAX_RESULTS_PER_MAIL = 20;
static interface MailSender {
void send(MimeMessage message) throws MessagingException;
}

private static final int MAX_RESULTS_PER_MAIL = 20;
private final String recipients;
private final boolean sendToCulprits;
private MailSender mailSender = new RegressionReportNotifier.MailSender() {
@Override
public void send(MimeMessage message) throws MessagingException {
Transport.send(message);
}
};

@DataBoundConstructor
public RegressionReportNotifier(String recipients) {
public RegressionReportNotifier(String recipients, boolean sendToCulprits) {
this.recipients = recipients;
this.sendToCulprits = sendToCulprits;
}

@VisibleForTesting
void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}

@Override
Expand All @@ -59,6 +79,10 @@ public String getRecipients() {
return recipients;
}

public boolean getSendToCulprits() {
return sendToCulprits;
}

@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException {
Expand Down Expand Up @@ -112,7 +136,15 @@ private void mailReport(List<CaseResult> regressions, String recipients, BuildLi

// TODO link to test result page
StringBuilder builder = new StringBuilder();
builder.append(Util.encode(Jenkins.getInstance().getRootUrl()));
String rootUrl = "";
Session session = null;
InternetAddress adminAddress = null;
if (Jenkins.getInstance() != null) {
rootUrl = Jenkins.getInstance().getRootUrl();
session = Mailer.descriptor().createSession();
adminAddress = new InternetAddress(Mailer.descriptor().getAdminAddress());
}
builder.append(Util.encode(rootUrl));
builder.append(Util.encode(build.getUrl()));
builder.append("\n\n");
builder.append(regressions.size() + " regressions found.");
Expand All @@ -127,31 +159,45 @@ private void mailReport(List<CaseResult> regressions, String recipients, BuildLi
builder.append(" ...");
builder.append("\n");
}
List<Address> recipentList = parse(recipients, listener);
if (sendToCulprits) {
recipentList.addAll(loadAddrOfCulprits(build, listener));
}

MimeMessage message = new MimeMessage(Mailer.descriptor().createSession());
MimeMessage message = new MimeMessage(session);
message.setSubject(Messages.RegressionReportNotifier_MailSubject());
message.setRecipients(RecipientType.TO, convertToAddr(recipients, listener));
message.setRecipients(RecipientType.TO, recipentList.toArray(new Address[recipentList.size()]));
message.setContent("", "text/plain");
message.setFrom(new InternetAddress(Mailer.descriptor().getAdminAddress()));
message.setFrom(adminAddress);
message.setText(builder.toString());
message.setSentDate(new Date());

Transport.send(message);
mailSender.send(message);
}

private Set<Address> loadAddrOfCulprits(AbstractBuild<?, ?> build,
BuildListener listener) {
Set<User> authorSet = Sets.newHashSet(transform(
build.getChangeSet(),
new ChangeSetToAuthor()));
Set<Address> addressSet = Sets.newHashSet(transform(
authorSet, new UserToAddr(listener.getLogger())));
return addressSet;
}

private Address[] convertToAddr(String recipients, BuildListener listener) {
Set<InternetAddress> set = Sets.newHashSet();
private List<Address> parse(String recipients, BuildListener listener) {
List<Address> list = Lists.newArrayList();
StringTokenizer tokens = new StringTokenizer(recipients);
while (tokens.hasMoreTokens()) {
String address = tokens.nextToken();
try {
set.add(new InternetAddress(address));
list.add(new InternetAddress(address));
} catch (AddressException e) {
e.printStackTrace(listener.error(e.getMessage()));
}
}

return set.toArray(new Address[set.size()]);
return list;
}

@VisibleForTesting
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/jp/skypencil/jenkins/regression/UserIdToAddr.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,25 @@
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

import jenkins.model.Jenkins;

import com.google.common.base.Function;

/**
* Convert user id to email address. Returned value can be null.
*/
final class UserIdToAddr implements Function<String, Address> {
private final String defaultSuffix = Mailer.descriptor().getDefaultSuffix();
private final String defaultSuffix;
private final PrintStream logger;

UserIdToAddr(PrintStream logger) {
this.logger = checkNotNull(logger);
if (Jenkins.getInstance() != null) {
defaultSuffix = Mailer.descriptor().getDefaultSuffix();
} else {
// for debug
defaultSuffix = "@mail.com";
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
<f:entry field="recipients" title="${%Recipients}">
<f:textbox />
</f:entry>
<f:entry field="sendToCulprits" title="${%SentToCulprits}">
<f:checkbox />
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Recipients=Recipients
SentToCulprits=Send mail to who might make regression
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Recipients=\u5b9b\u5148
SentToCulprits=\u30D3\u30EB\u30C9\u3092\u58CA\u3057\u305F\u304B\u3082\u3057\u308C\u306A\u3044\u30E6\u30FC\u30B6\u306B\u3082\u30E1\u30FC\u30EB\u3059\u308B
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package jp.skypencil.jenkins.regression;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;

import com.google.common.collect.Sets;

import hudson.model.AbstractBuild;
import hudson.model.User;
import hudson.scm.ChangeLogSet;

public class ChangeLogSetMock extends ChangeLogSet<ChangeLogSet.Entry> {

protected ChangeLogSetMock(AbstractBuild<?, ?> build) {
super(build);
}

private final Set<Entry> set = Sets.newHashSet();

ChangeLogSetMock withChangeBy(final User user) {
ChangeLogSet.Entry change = new ChangeLogSet.Entry() {
@Override
public String getMsg() {
return "";
}
@Override
public User getAuthor() {
return user;
}
@Override
public Collection<String> getAffectedPaths() {
return Collections.emptyList();
}
};
set.add(change);
return this;
}

@Override
public Iterator<Entry> iterator() {
return set.iterator();
}

@Override
public boolean isEmptySet() {
return set.isEmpty();
}

}
Original file line number Diff line number Diff line change
@@ -1,38 +1,117 @@
package jp.skypencil.jenkins.regression;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.Mockito.*;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.powermock.api.mockito.PowerMockito.doReturn;
import static org.powermock.api.mockito.PowerMockito.mock;
import hudson.Launcher;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.User;
import hudson.tasks.junit.CaseResult;
import hudson.tasks.junit.CaseResult.Status;
import hudson.tasks.test.AbstractTestResultAction;

import java.io.IOException;
import java.io.PrintStream;
import java.util.List;

import javax.mail.Address;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.google.common.collect.Lists;

@RunWith(PowerMockRunner.class)
@PrepareForTest(CaseResult.class)
public class RegressionReportNotifierTest {
private BuildListener listener;
private Launcher launcher;
private AbstractBuild<?, ?> build;

@Before
public void setUp() {
public void setUp() throws Exception {
listener = mock(BuildListener.class);
launcher = mock(Launcher.class);
build = mock(AbstractBuild.class);
PrintStream logger = mock(PrintStream.class);
doReturn("").when(build).getUrl();
doReturn(logger).when(listener).getLogger();
}

@Test
public void testCompileErrorOccured() throws InterruptedException, IOException {
doReturn(null).when(build).getTestResultAction();
RegressionReportNotifier notifier = new RegressionReportNotifier("");
RegressionReportNotifier notifier = new RegressionReportNotifier("", false);

assertThat(notifier.perform(build, launcher, listener), is(true));
}

@Test
public void testSend() throws InterruptedException, MessagingException {
makeRegression();

RegressionReportNotifier notifier = new RegressionReportNotifier("author@mail.com", false);
MockedMailSender mailSender = new MockedMailSender();
notifier.setMailSender(mailSender);

assertThat(notifier.perform(build, launcher, listener), is(true));
assertThat(mailSender.getSentMessage(), is(notNullValue()));
Address[] to = mailSender.getSentMessage().getRecipients(RecipientType.TO);
assertThat(to.length, is(1));
assertThat(to[0].toString(), is(equalTo("author@mail.com")));
}

@Test
public void testSendToCulprits() throws InterruptedException, MessagingException {
makeRegression();

RegressionReportNotifier notifier = new RegressionReportNotifier("author@mail.com", true);
MockedMailSender mailSender = new MockedMailSender();
notifier.setMailSender(mailSender);

assertThat(notifier.perform(build, launcher, listener), is(true));
assertThat(mailSender.getSentMessage(), is(notNullValue()));
Address[] to = mailSender.getSentMessage().getRecipients(RecipientType.TO);
assertThat(to.length, is(2));
assertThat(to[0].toString(), is(equalTo("author@mail.com")));
assertThat(to[1].toString(), is(equalTo("culprit@mail.com")));
}

private void makeRegression() {
AbstractTestResultAction<?> result = mock(AbstractTestResultAction.class);
doReturn(result).when(build).getTestResultAction();
doReturn(Result.FAILURE).when(build).getResult();
User culprit = mock(User.class);
doReturn("culprit").when(culprit).getId();
doReturn(new ChangeLogSetMock(build).withChangeBy(culprit)).when(build).getChangeSet();

CaseResult failedTest = mock(CaseResult.class);
doReturn(Status.REGRESSION).when(failedTest).getStatus();
List<CaseResult> failedTests = Lists.newArrayList(failedTest);
doReturn(failedTests).when(result).getFailedTests();
}

private static final class MockedMailSender implements RegressionReportNotifier.MailSender {
private MimeMessage sentMessage;

@Override
public void send(MimeMessage message) throws MessagingException {
sentMessage = message;
}

public MimeMessage getSentMessage() {
return sentMessage;
}
}
}

0 comments on commit d4d2473

Please sign in to comment.