diff --git a/core/src/main/java/hudson/logging/LogRecorderManager.java b/core/src/main/java/hudson/logging/LogRecorderManager.java index 485a31ab48d4481395bda2145bf54293f34b0a0e..4bf9730f3a2a645fa2a0d60d3b203ced3c618a0c 100644 --- a/core/src/main/java/hudson/logging/LogRecorderManager.java +++ b/core/src/main/java/hudson/logging/LogRecorderManager.java @@ -157,6 +157,7 @@ public class LogRecorderManager extends AbstractModelObject implements ModelObje */ /*package*/ static void doRss(StaplerRequest req, StaplerResponse rsp, List logs) throws IOException, ServletException { // filter log records based on the log level + String entryType = "all"; String level = req.getParameter("level"); if(level!=null) { Level threshold = Level.parse(level); @@ -166,9 +167,10 @@ public class LogRecorderManager extends AbstractModelObject implements ModelObje filtered.add(r); } logs = filtered; + entryType = level; } - RSS.forwardToRss("Hudson log","", logs, new FeedAdapter() { + RSS.forwardToRss("Jenkins:log (" + entryType + " entries)","", logs, new FeedAdapter() { public String getEntryTitle(LogRecord entry) { return entry.getMessage(); } diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 34f59b99da5a5dfbc6141d1b725a2a77c508df3a..de123b936887c8688f9cb805261328f8f7a40664 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -1371,12 +1371,12 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces // @Restricted(DoNotUse.class) public void doRssAll( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { - RSS.rss(req, rsp, getDisplayName() + " all builds", getUrl(), getBuilds()); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (all builds)", getUrl(), getBuilds()); } @Restricted(DoNotUse.class) public void doRssFailed(StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { - RSS.rss(req, rsp, getDisplayName() + " failed builds", getUrl(), getBuilds().failureOnly()); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (failed builds)", getUrl(), getBuilds().failureOnly()); } /** @@ -1398,7 +1398,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces } } } - RSS.rss(req, rsp, getDisplayName() + " last builds only", getUrl(), RunList.fromRuns(lastBuilds)); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (latest builds)", getUrl(), RunList.fromRuns(lastBuilds)); } @RequirePOST diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index 80b73062c44283a2563e5df26b570e7cce21dae2..caddc5ca607bd64a2bf95755b91595187fd90446 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -1566,12 +1566,12 @@ public abstract class Job, RunT extends Run"); - pw.println("" + (url != null ? 0 : 1) + ""); - if (url == null) { - pw.println("url must be specified"); - } - pw.println(""); - } - } - /** * Sends the RSS feed to the client. * diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java index c49c795ed20bf1262880a5e7f72d0bfe2e413257..e0aec706ffdd25ac821e06f0154ac89a03412eb5 100644 --- a/core/src/main/java/hudson/model/User.java +++ b/core/src/main/java/hudson/model/User.java @@ -871,11 +871,11 @@ public class User extends AbstractModelObject implements AccessControlled, Descr } public void doRssAll(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - RSS.rss(req, rsp, getDisplayName() + " all builds", getUrl(), getBuilds().newBuilds()); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (all builds)", getUrl(), getBuilds().newBuilds()); } public void doRssFailed(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - RSS.rss(req, rsp, getDisplayName() + " regression builds", getUrl(), getBuilds().regressionOnly()); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (failed builds)", getUrl(), getBuilds().regressionOnly()); } public void doRssLatest(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { @@ -891,7 +891,7 @@ public class User extends AbstractModelObject implements AccessControlled, Descr // historically these have been reported sorted by project name, we switched to the lazy iteration // so we only have to sort the sublist of runs rather than the full list of irrelevant projects lastBuilds.sort((o1, o2) -> Items.BY_FULL_NAME.compare(o1.getParent(), o2.getParent())); - RSS.rss(req, rsp, getDisplayName() + " latest build", getUrl(), RunList.fromRuns(lastBuilds), Run.FEED_ADAPTER_LATEST); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (latest builds)", getUrl(), RunList.fromRuns(lastBuilds), Run.FEED_ADAPTER_LATEST); } @Override diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index f51a3dd9586fc9d18c3025903d0b73a816d09130..190eb985f927aa51e91ecc99ae6fbce50432c040 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -1143,11 +1143,11 @@ public abstract class View extends AbstractModelObject implements AccessControll } public void doRssAll( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { - RSS.rss(req, rsp, getDisplayName() + " all builds", getUrl(), getBuilds().newBuilds()); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (all builds)", getUrl(), getBuilds().newBuilds()); } public void doRssFailed( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { - RSS.rss(req, rsp, getDisplayName() + " failed builds", getUrl(), getBuilds().failureOnly().newBuilds()); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (failed builds)", getUrl(), getBuilds().failureOnly().newBuilds()); } public RunList getBuilds() { @@ -1167,7 +1167,7 @@ public abstract class View extends AbstractModelObject implements AccessControll if(lb!=null) lastBuilds.add(lb); } } - RSS.rss(req, rsp, getDisplayName() + " last builds only", getUrl(), RunList.fromRuns(lastBuilds), Run.FEED_ADAPTER_LATEST); + RSS.rss(req, rsp, "Jenkins:" + getDisplayName() + " (latest builds)", getUrl(), RunList.fromRuns(lastBuilds), Run.FEED_ADAPTER_LATEST); } /** diff --git a/test/src/test/java/hudson/model/RSSTest.java b/test/src/test/java/hudson/model/RSSTest.java index 3967e94a7d8c5ebb82d4a09890b390a5a408206e..f7a586c02a6bf35cfc12c6136dec378a93941f34 100644 --- a/test/src/test/java/hudson/model/RSSTest.java +++ b/test/src/test/java/hudson/model/RSSTest.java @@ -24,68 +24,134 @@ package hudson.model; import com.gargoylesoftware.htmlunit.xml.XmlPage; +import jenkins.model.Jenkins; import org.junit.Rule; import org.junit.Test; +import org.jvnet.hudson.test.FailureBuilder; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.OffsetDateTime; import java.util.ArrayList; +import java.util.Date; import java.util.List; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; public class RSSTest { + private static final String ROOT_PATH_PREFIX = ""; + private static final String FAILED_BUILD_TITLE = "test0 #1 (broken since this build)"; + private static final String STABLE_BUILD_TITLE = "test0 #1 (stable)"; + private static final String ALL_BUILD_TYPE = "all"; + private static final String FAILED_BUILD_TYPE = "failed"; + private static final String LATEST_BUILD_TYPE = "latest"; + @Rule public JenkinsRule j = new JenkinsRule(); @Test @Issue("JENKINS-59167") - public void absoluteURLsPresentInRSS_evenWithoutRootUrlSetup() throws Exception { - XmlPage page = getRssAllPage(); + public void absoluteURLsPresentInRSS_evenWithoutRootUrlSetup_View() throws Exception { + String pathPrefix = ROOT_PATH_PREFIX; + XmlPage page = getRssAllPage(pathPrefix); NodeList allLinks = page.getXmlDocument().getElementsByTagName("link"); assertEquals(1, allLinks.getLength()); assertAllRSSLinksContainRootUrl(allLinks); - FreeStyleProject p = j.createFreeStyleProject(); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + runSuccessfulBuild(); - page = getRssAllPage(); + page = getRssAllPage(pathPrefix); allLinks = page.getXmlDocument().getElementsByTagName("link"); assertEquals(2, allLinks.getLength()); assertAllRSSLinksContainRootUrl(allLinks); } - private XmlPage getRssAllPage() throws Exception { - return (XmlPage) j.createWebClient().goTo("rssAll?flavor=rss20", "text/xml"); + @Test + public void checkInitialContent_Rss_All_AllView() throws Exception { + String pathPrefix = ROOT_PATH_PREFIX; + int expectedNodes = 3; + XmlPage page = getRssAllPage(pathPrefix); + Node channelNode = checkRssWrapperNodes(page.getXmlDocument()); + checkRssBasicNodes(channelNode, "Jenkins:All (all builds)", expectedNodes, pathPrefix); } - private void assertAllRSSLinksContainRootUrl(NodeList allLinks) throws Exception { - for (int i = 0; i < allLinks.getLength(); i++) { - Node item = allLinks.item(i); - String url = item.getTextContent(); - assertThat(url, containsString(j.getURL().toString())); - } + @Test + public void checkInitialContent_Rss_Failed_AllView() throws Exception { + String pathPrefix = ROOT_PATH_PREFIX; + int expectedNodes = 3; + XmlPage page = getRssFailedPage(pathPrefix); + Node channelNode = checkRssWrapperNodes(page.getXmlDocument()); + checkRssBasicNodes(channelNode, "Jenkins:All (failed builds)", expectedNodes, pathPrefix); + } + + @Test + public void checkInitialContent_Atom_All_AllView() throws Exception { + int expectedNodes = 5; + XmlPage page = getRssAllAtomPage(); + Element documentElement = page.getXmlDocument().getDocumentElement(); + assertThat(documentElement.getNodeName(), is("feed")); + checkAtomBasicNodes(documentElement, "Jenkins:All (all builds)", expectedNodes); + } + + @Test + public void checkWithSingleBuild_Rss_All_AllView() throws Exception { + runSuccessfulBuild(); + + String pathPrefix = ROOT_PATH_PREFIX; + String displayName = "All"; + String buildType = ALL_BUILD_TYPE; + String buildTitle = STABLE_BUILD_TITLE; + XmlPage page = getRssAllPage(pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void checkWithSingleBuild_Rss_Failed_AllView() throws Exception { + runFailingBuild(); + + String pathPrefix = ROOT_PATH_PREFIX; + String displayName = "All"; + String buildType = FAILED_BUILD_TYPE; + String buildTitle = FAILED_BUILD_TITLE; + XmlPage page = getRssFailedPage(pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void checkWithSingleBuild_Atom_All_AllView() throws Exception { + runSuccessfulBuild(); + + String displayName = "All"; + String buildType = ALL_BUILD_TYPE; + String buildTitle = STABLE_BUILD_TITLE; + XmlPage page = getRssAllAtomPage(); + checkSingleBuild_Atom(page, displayName, buildType, buildTitle); } @Test @Issue("JENKINS-59167") - public void absoluteURLsPresentInAtom_evenWithoutRootUrlSetup() throws Exception { + public void absoluteURLsPresentInAtom_evenWithoutRootUrlSetup_View() throws Exception { XmlPage page = getRssAllAtomPage(); NodeList allLinks = page.getXmlDocument().getElementsByTagName("link"); assertEquals(1, allLinks.getLength()); assertAllAtomLinksContainRootUrl(allLinks); - FreeStyleProject p = j.createFreeStyleProject(); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + runSuccessfulBuild(); page = getRssAllAtomPage(); allLinks = page.getXmlDocument().getElementsByTagName("link"); @@ -94,6 +160,284 @@ public class RSSTest { assertAllAtomLinksContainRootUrl(allLinks); } + @Issue("JENKINS-60577") + @Test + public void latestBuilds_AllView() throws Exception { + String pathPrefix = ROOT_PATH_PREFIX; + String displayName = "All"; + String buildType = LATEST_BUILD_TYPE; + int expectedLinks = 3; + int expectedAllLinks = 6; + checkLatestBuilds(j.createWebClient(), pathPrefix, displayName, buildType, null, expectedLinks, expectedAllLinks); + } + + @Test + public void checkWithSingleBuild_Rss_All_Computer() throws Exception { + runSuccessfulBuild(); + + String pathPrefix = "computer/(master)/"; + String displayName = "master"; + String buildType = ALL_BUILD_TYPE; + String buildTitle = STABLE_BUILD_TITLE; + XmlPage page = getRssAllPage(pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void checkWithSingleBuild_Rss_Failed_Computer() throws Exception { + runFailingBuild(); + + String pathPrefix = "computer/(master)/"; + String displayName = "master"; + String buildType = FAILED_BUILD_TYPE; + String buildTitle = FAILED_BUILD_TITLE; + XmlPage page = getRssFailedPage(pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void latestBuilds_Computer() throws Exception { + String pathPrefix = "computer/(master)/"; + String displayName = "master"; + String buildType = LATEST_BUILD_TYPE; + int expectedLinks = 3; + int expectedAllLinks = 6; + checkLatestBuilds(j.createWebClient(), pathPrefix, displayName, buildType, null, expectedLinks, expectedAllLinks); + } + + @Test + public void checkWithSingleBuild_Rss_All_Job() throws Exception { + runSuccessfulBuild(); + + String pathPrefix = "job/test0/"; + String displayName = "test0"; + String buildType = ALL_BUILD_TYPE; + String buildTitle = STABLE_BUILD_TITLE; + XmlPage page = getRssAllPage(pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void checkWithSingleBuild_Rss_Failed_Job() throws Exception { + runFailingBuild(); + + String pathPrefix = "job/test0/"; + String displayName = "test0"; + String buildType = FAILED_BUILD_TYPE; + String buildTitle = FAILED_BUILD_TITLE; + XmlPage page = getRssFailedPage(pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void checkWithSingleBuild_Rss_All_NewView() throws Exception { + FreeStyleProject p = runSuccessfulBuild(); + + ListView newView = new ListView("newView"); + j.jenkins.addView(newView); + newView.add(p); + + String pathPrefix = "view/newView/"; + String displayName = "newView"; + String buildType = ALL_BUILD_TYPE; + String buildTitle = STABLE_BUILD_TITLE; + XmlPage page = getRssAllPage(pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void checkWithSingleBuild_Rss_All_User() throws Exception { + String userId = "alice"; + JenkinsRule.WebClient wc = loginAsUser(userId); + + runSuccessfulBuild(userId); + + String pathPrefix = "user/alice"; + String displayName = userId; + String buildType = ALL_BUILD_TYPE; + String buildTitle = STABLE_BUILD_TITLE; + XmlPage page = getRssAllPage(wc, pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void checkWithSingleBuild_Rss_Failed_User() throws Exception { + String userId = "alice"; + JenkinsRule.WebClient wc = loginAsUser(userId); + + runFailingBuild(userId); + + String pathPrefix = "user/alice"; + String displayName = userId; + String buildType = FAILED_BUILD_TYPE; + String buildTitle = FAILED_BUILD_TITLE; + XmlPage page = getRssFailedPage(wc, pathPrefix); + checkSingleBuild_Rss(page, pathPrefix, displayName, buildType, buildTitle); + } + + @Test + public void latestBuilds_User() throws Exception { + String userId = "alice"; + JenkinsRule.WebClient wc = loginAsUser(userId); + String pathPrefix = "user/alice"; + String displayName = userId; + String buildType = LATEST_BUILD_TYPE; + int expectedLinks = 3; + int expectedAllLinks = 6; + checkLatestBuilds(wc, pathPrefix, displayName, buildType, userId, expectedLinks, expectedAllLinks); + } + + @Test + public void latestBuilds_User_NotCaused() throws Exception { + String userId = "alice"; + JenkinsRule.WebClient wc = loginAsUser(userId); + String pathPrefix = "user/alice"; + String displayName = userId; + String buildType = LATEST_BUILD_TYPE; + int expectedLinks = 1; + int expectedAllLinks = 1; + checkLatestBuilds(wc, pathPrefix, displayName, buildType, null, expectedLinks, expectedAllLinks); + } + + @Test + public void checkInitialContent_Atom_AllLog() throws Exception { + JenkinsRule.WebClient webClient = j.createWebClient(); + XmlPage page = (XmlPage) webClient.goTo("log/rss", "application/atom+xml"); + Document xmlDocument = page.getXmlDocument(); + Element documentElement = xmlDocument.getDocumentElement(); + assertThat(documentElement.getNodeName(), is("feed")); + int expectedNodes = 5; + checkAtomBasicNodes(documentElement, "Jenkins:log (all entries)", expectedNodes); + } + + @Test + public void checkInitialContent_Atom_SevereLog() throws Exception { + JenkinsRule.WebClient webClient = j.createWebClient(); + XmlPage page = (XmlPage) webClient.goTo("log/rss?level=SEVERE", "application/atom+xml"); + Document xmlDocument = page.getXmlDocument(); + Element documentElement = xmlDocument.getDocumentElement(); + assertThat(documentElement.getNodeName(), is("feed")); + int expectedNodes = 5; + checkAtomBasicNodes(documentElement, "Jenkins:log (SEVERE entries)", expectedNodes); + } + + @Test + public void checkInitialContent_Atom_WarningLog() throws Exception { + JenkinsRule.WebClient webClient = j.createWebClient(); + XmlPage page = (XmlPage) webClient.goTo("log/rss?level=WARNING", "application/atom+xml"); + Document xmlDocument = page.getXmlDocument(); + Element documentElement = xmlDocument.getDocumentElement(); + assertThat(documentElement.getNodeName(), is("feed")); + int expectedNodes = 5; + checkAtomBasicNodes(documentElement, "Jenkins:log (WARNING entries)", expectedNodes); + } + + private JenkinsRule.WebClient loginAsUser(String userId) throws Exception { + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to(userId)); + User alice = User.getById(userId, true); + JenkinsRule.WebClient wc = j.createWebClient(); + wc.login(alice.getId()); + return wc; + } + + private Node checkRssWrapperNodes(Document xmlDocument) { + Element documentElement = xmlDocument.getDocumentElement(); + assertThat(documentElement.getNodeName(), is("rss")); + assertThat(documentElement.getAttribute("version"), is("2.0")); + assertThat(documentElement.getChildNodes().getLength(), is(1)); + Node channelNode = documentElement.getFirstChild(); + assertThat(channelNode.getNodeName(), is("channel")); + return channelNode; + } + + private XmlPage getRssFailedPage(String pathPrefix) throws IOException, SAXException { + return getRssFailedPage(j.createWebClient(), pathPrefix); + } + + private XmlPage getRssFailedPage(JenkinsRule.WebClient webClient, String pathPrefix) throws IOException, SAXException { + String prefix = computeAdjustedPathPrefix(pathPrefix); + return (XmlPage) webClient.goTo(prefix + "rssFailed?flavor=rss20", "text/xml"); + } + + private void checkRssBasicNodes(Node channelNode, String expectedTitle, int expectedNodes, String path) throws IOException { + assertThat(channelNode.getChildNodes().getLength(), is(expectedNodes)); + assertThat(getSingleNode(channelNode, "link").getTextContent(), is(j.getURL().toString() + path)); + assertThat(getSingleNode(channelNode, "description").getTextContent(), is(expectedTitle)); + assertThat(getSingleNode(channelNode, "title").getTextContent(), is(expectedTitle)); + } + + private void checkAtomBasicNodes(Node feedNode, String expectedTitle, int expectedNodes) throws IOException { + if (expectedNodes >= 0) { + assertThat(feedNode.getChildNodes().getLength(), is(expectedNodes)); + } + Node linkNode = getSingleNode(feedNode, "link"); + assertThat(linkNode.getAttributes().getNamedItem("rel").getTextContent(), is("alternate")); + assertThat(linkNode.getAttributes().getNamedItem("type").getTextContent(), is("text/html")); + assertThat(linkNode.getAttributes().getNamedItem("href").getTextContent(), is(j.getURL().toString())); + assertNotNull(getSingleNode(feedNode, "updated")); + assertThat(getSingleNode(feedNode, "title").getTextContent(), is(expectedTitle)); + Node authorNode = getSingleNode(feedNode, "author"); + NodeList authorNodes = authorNode.getChildNodes(); + assertThat(authorNodes.getLength(), is(1)); + Node nameNode = authorNodes.item(0); + assertThat(nameNode.getTextContent(), is("Jenkins Server")); + Node idNode = getSingleNode(feedNode, "id"); + assertFalse(idNode.getTextContent().isEmpty()); + } + + private Node getSingleNode(Node parentNode, String nodeName) { + Node childNode = null; + NodeList childNodes = parentNode.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + if (childNodes.item(i).getNodeName().equals(nodeName)) { + if (childNode == null) { + childNode = childNodes.item(i); + } else { + fail("Too many children."); + } + } + } + return childNode; + } + + private void checkRssTimeNode(Node firstBuild, String nodeName) throws ParseException { + String pubDate = getSingleNode(firstBuild, nodeName).getTextContent(); + assertThat(pubDate, not(emptyString())); + DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); + Date date = formatter.parse(pubDate); + assertNotNull(date); + } + + private void checkAtomTimeNode(Node firstBuild, String nodeName) { + String publishedString = getSingleNode(firstBuild, nodeName).getTextContent(); + assertNotNull(publishedString); + assertThat(publishedString, is(not(emptyString()))); + OffsetDateTime dateTime = OffsetDateTime.parse(publishedString); + assertNotNull(dateTime); + } + + private XmlPage getRssAllPage(String pathPrefix) throws Exception { + return getRssAllPage(j.createWebClient(), pathPrefix); + } + + private XmlPage getRssAllPage(JenkinsRule.WebClient webClient, String pathPrefix) throws Exception { + String prefix = computeAdjustedPathPrefix(pathPrefix); + return (XmlPage) webClient.goTo(prefix + "rssAll?flavor=rss20", "text/xml"); + } + + private String computeAdjustedPathPrefix(String pathPrefix) { + return pathPrefix.isEmpty() || pathPrefix.endsWith("/") ? pathPrefix : pathPrefix + "/"; + } + + private void assertAllRSSLinksContainRootUrl(NodeList allLinks) throws Exception { + for (int i = 0; i < allLinks.getLength(); i++) { + Node item = allLinks.item(i); + String url = item.getTextContent(); + assertThat(url, containsString(j.getURL().toString())); + } + } + private XmlPage getRssAllAtomPage() throws Exception { return (XmlPage) j.createWebClient().goTo("rssAll", "application/atom+xml"); } @@ -107,39 +451,36 @@ public class RSSTest { } } - @Issue("JENKINS-60577") - @Test - public void latestBuilds() throws Exception { - XmlPage page = getRssLatestPage(); - NodeList allLinks = page.getXmlDocument().getElementsByTagName("link"); - - assertEquals(1, allLinks.getLength()); - - FreeStyleProject p = j.createFreeStyleProject("test1"); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + private XmlPage getRssLatestPage(JenkinsRule.WebClient webClient, String pathPrefix) throws Exception { + String prefix = computeAdjustedPathPrefix(pathPrefix); + return (XmlPage) webClient.goTo(prefix + "rssLatest?flavor=rss20", "text/xml"); + } - p = j.createFreeStyleProject("test2"); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + private FreeStyleProject runSuccessfulBuild() throws Exception { + FreeStyleProject p = j.createFreeStyleProject(); j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + return p; + } - page = getRssLatestPage(); - allLinks = page.getXmlDocument().getElementsByTagName("link"); - - assertEquals(3, allLinks.getLength()); - assertLatestRSSLinks(allLinks); + private FreeStyleProject runSuccessfulBuild(String userId) throws Exception { + FreeStyleProject p = j.createFreeStyleProject(); + j.assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause(userId))); + return p; + } - page = getRssAllPage(); - allLinks = page.getXmlDocument().getElementsByTagName("link"); - assertEquals(6, allLinks.getLength()); + private void runFailingBuild() throws Exception { + FreeStyleProject p = j.createFreeStyleProject(); + p.getBuildersList().add(new FailureBuilder()); + j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); } - private XmlPage getRssLatestPage() throws Exception { - return (XmlPage) j.createWebClient().goTo("rssLatest?flavor=rss20", "text/xml"); + private void runFailingBuild(String userId) throws Exception { + FreeStyleProject p = j.createFreeStyleProject(); + p.getBuildersList().add(new FailureBuilder()); + j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0, new Cause.UserIdCause(userId)).get()); } - private void assertLatestRSSLinks(NodeList allLinks) throws Exception { + private void assertLatestRSSLinks(NodeList allLinks, String path) throws Exception { List urls = new ArrayList<>(allLinks.getLength()); for (int i = 0; i < allLinks.getLength(); i++) { Node item = allLinks.item(i); @@ -148,9 +489,76 @@ public class RSSTest { } assertThat(urls, containsInAnyOrder( - j.getURL().toString(), + j.getURL().toString() + path, j.getURL().toString() + "job/test1/2/", j.getURL().toString() + "job/test2/3/" )); } + + private void checkSingleBuild_Rss(XmlPage page, String pathPrefix, String displayName, String buildType, String buildTitle) throws Exception { + Document xmlDocument = page.getXmlDocument(); + Node channelNode = checkRssWrapperNodes(xmlDocument); + checkRssBasicNodes(channelNode, "Jenkins:" + displayName + " (" + buildType + " builds)", 4, pathPrefix); + NodeList items = xmlDocument.getElementsByTagName("item"); + assertThat(items.getLength(), is(1)); + Node firstBuild = items.item(0); + assertThat(firstBuild.getChildNodes().getLength(), is(5)); + assertThat(getSingleNode(firstBuild, "title").getTextContent(), is(buildTitle)); + checkRssTimeNode(firstBuild, "pubDate"); + assertNotNull(getSingleNode(firstBuild, "author").getTextContent()); + Node guidNode = getSingleNode(firstBuild, "guid"); + assertThat(guidNode.getAttributes().getNamedItem("isPermaLink").getTextContent(), is("false")); + assertNotNull(guidNode.getTextContent()); + } + + private void checkSingleBuild_Atom(XmlPage page, String displayName, String buildType, String buildTitle) throws IOException { + Document xmlDocument = page.getXmlDocument(); + Element documentElement = xmlDocument.getDocumentElement(); + assertThat(documentElement.getNodeName(), is("feed")); + checkAtomBasicNodes(documentElement, "Jenkins:" + displayName + " (" + buildType + " builds)", 6); + NodeList entries = xmlDocument.getElementsByTagName("entry"); + assertThat(entries.getLength(), is(1)); + Node firstBuild = entries.item(0); + assertThat(firstBuild.getChildNodes().getLength(), is(5)); + assertThat(getSingleNode(firstBuild, "title").getTextContent(), is(buildTitle)); + checkAtomTimeNode(firstBuild, "published"); + checkAtomTimeNode(firstBuild, "updated"); + assertNotNull(getSingleNode(firstBuild, "id").getTextContent()); + Node linkNode = getSingleNode(firstBuild, "link"); + assertThat(linkNode.getAttributes().getNamedItem("rel").getTextContent(), is("alternate")); + assertThat(linkNode.getAttributes().getNamedItem("type").getTextContent(), is("text/html")); + assertThat(linkNode.getAttributes().getNamedItem("href").getTextContent(), containsString(j.getURL().toString())); + } + + private void checkLatestBuilds(JenkinsRule.WebClient webClient, String pathPrefix, String displayName, String buildType, String userId, + int expectedLatesLinks, int expectedAllLinks) throws Exception { + XmlPage page = getRssLatestPage(webClient, pathPrefix); + NodeList allLinks = page.getXmlDocument().getElementsByTagName("link"); + + assertEquals(1, allLinks.getLength()); + Node channelNode = checkRssWrapperNodes(page.getXmlDocument()); + checkRssBasicNodes(channelNode, "Jenkins:" + displayName + " (" + buildType + " builds)", 3, pathPrefix); + + FreeStyleProject p = j.createFreeStyleProject("test1"); + j.assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause(userId))); + j.assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause(userId))); + + p = j.createFreeStyleProject("test2"); + j.assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause(userId))); + j.assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause(userId))); + j.assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause(userId))); + + page = getRssLatestPage(webClient, pathPrefix); + allLinks = page.getXmlDocument().getElementsByTagName("link"); + + assertEquals(expectedLatesLinks, allLinks.getLength()); + if (expectedLatesLinks > 1) { + assertLatestRSSLinks(allLinks, pathPrefix); + } + + page = getRssAllPage(webClient, pathPrefix); + allLinks = page.getXmlDocument().getElementsByTagName("link"); + assertEquals(expectedAllLinks, allLinks.getLength()); + } + }