/* * The MIT License * * Copyright (c) 2010, Yahoo!, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.plugins.labeledandgroupedtests; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlTable; import com.gargoylesoftware.htmlunit.html.HtmlAnchor; import com.gargoylesoftware.htmlunit.Page; import hudson.tasks.test.*; import hudson.tasks.junit.*; import hudson.tasks.junit.PackageResult; import hudson.model.*; import hudson.plugins.labeledgroupedtests.MetaLabeledTestResultGroupAction; import hudson.plugins.labeledgroupedtests.MetaLabeledTestResultGroup; import hudson.plugins.labeledgroupedtests.LabeledTestResultGroup; import hudson.slaves.DumbSlave; import hudson.tasks.test.TestResult; import org.jvnet.hudson.test.HudsonTestCase; import org.jvnet.hudson.test.recipes.LocalData; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; /** * Exercise a project with a known configuration using multiple test * formats and multiple test phases */ public class CombinationOfParsersAndLabelsTest extends EnhancedHudsonTestCase { private static final String COMBO_PROJECT_NAME = "combo"; private static final String JUST_JAVA_GROUPS = "just_java_groups"; private static final String CAT_PROJECT_NAME = "cat"; private void SKIP_buildComboProjectAndValidate(FreeStyleProject freestyleProj) throws Exception { FreeStyleBuild build = freestyleProj.scheduleBuild2(0).get(10, TimeUnit.SECONDS); // Hey! we should *not* have build success! Dude! There's a test failure! assertBuildStatus(Result.UNSTABLE, build); // I need an action MetaLabeledTestResultGroupAction action = build.getAction(MetaLabeledTestResultGroupAction.class); assertNotNull("we should have an action", action); // Look for a MetaLabeledTestResultGroup at the top level MetaLabeledTestResultGroup result = action.getResultAsTestResultGroup(); assertNotNull("we should have a non-null result group", result); assertTrue( "should have at least one test", result.getTotalCount() > 0); assertTrue( "should have at least one passing test", result.getPassCount() > 0); assertTrue( "should have at least one failing test", result.getFailCount() > 0); assertEquals("should have zero skipped tests", 0, result.getSkipCount() ); HudsonTestCase.WebClient wc = new HudsonTestCase.WebClient(); // On the project page: HtmlPage projectPage = wc.getPage(freestyleProj); assertGoodStatus(projectPage); HtmlPage buildPage = wc.goTo(freestyleProj.getLastBuild().getUrl() ); assertGoodStatus(buildPage); HtmlPage testReportPage = wc.goTo(freestyleProj.getLastBuild().getUrl() + "/testReport/"); assertGoodStatus(testReportPage); // we should have a link that reads "Latest Test Result" // that link should go to http://localhost:8080/job/breakable/lastBuild/testReport/ // assertXPath(projectPage, "//a[@href='lastBuild/testReport/']"); // assertXPathValue(projectPage, "//a[@href='lastBuild/testReport/']", "Latest Test Result"); // assertXPathValueContains(projectPage, "//a[@href='lastBuild/testReport/']", "Latest Test Result"); // // after "Latest Test Result" it should say "no failures" // assertXPathResultsContainText(projectPage, "//td", "(no failures)"); // // there should be a test result trend graph // assertXPath(projectPage, "//img[@src='test/trend']"); // // the trend graph should be served up with a good http status // Page trendGraphPage = wc.goTo(project.getUrl() + "/test/trend", "image/png"); // assertGoodStatus(trendGraphPage); } @LocalData public void SKIP_testPublishingTests() throws Exception { SKIP_buildComboProjectAndValidate(setupProject(COMBO_PROJECT_NAME)); } @LocalData public void SKIP_testRemotePublishingResults() throws Exception { FreeStyleProject freestyleProj = setupProject(COMBO_PROJECT_NAME); DumbSlave s = createOnlineSlave(); freestyleProj.setAssignedLabel(s.getSelfLabel()); setupRemoteData(COMBO_PROJECT_NAME, s, freestyleProj, "*.xml"); SKIP_buildComboProjectAndValidate(freestyleProj); } private void buildJavaGroupingProjectAndValidate(FreeStyleProject freestyleProj) throws Exception { FreeStyleBuild build = freestyleProj.scheduleBuild2(0).get(10, TimeUnit.SECONDS); // Hey! we should *not* have build success! Dude! There's a test failure! assertBuildStatus(Result.UNSTABLE, build); assertJavaProjectTestResults(build); } private void assertJavaProjectTestResults(FreeStyleBuild build) { // Make sure we've got some test results MetaLabeledTestResultGroupAction action = build.getAction(MetaLabeledTestResultGroupAction.class); assertNotNull("we should have an action", action); // Look for a MetaLabeledTestResultGroup at the top level MetaLabeledTestResultGroup result = action.getResultAsTestResultGroup(); assertNotNull("we should have a non-null result group", result); AbstractBuild<?,?> owner = result.getOwner(); assertNotNull("the result should have a non-null owner", owner); // Display correct *total* number of tests, not just the number of groups assertEquals( "should have 132 total groups of tests", 132, result.getTotalCount()); assertEquals( "should have zero skipped tests", 0, result.getSkipCount()); assertEquals( "should have exactly one failing test", 1, result.getFailCount()); // We want a non-zero duration assertTrue( "the tests should have duration of at least several seconds", result.getDuration() > 1.0f); // Drill down to the LabeledTestResultGroup // We know that there are labels "unit" and "smoke" LabeledTestResultGroup g = result.getGroupByLabel("unit"); assertNotNull("we have a 'unit' label, with children", g); assertEquals("expecting exactly 1 child of unit group", 1, g.getChildren().size()); assertEquals("unit test total children count", 39, g.getTotalCount()); assertEquals("unit test pass count", 39, g.getPassCount()); assertEquals("unit test fail count", 0, g.getFailCount()); TestResult firstChild = g.getChildByIndex(0); assertNotNull("we should have exactly one child", firstChild); LabeledTestResultGroup smoke = result.getGroupByLabel("smoke"); assertNotNull("we have a 'smoke' label, with children", smoke); assertEquals("expecting exactly 1 child of smoke group", 1, smoke.getChildren().size()); assertEquals("smoke test total children count", 81, smoke.getTotalCount()); assertEquals("smoke test pass count", 80, smoke.getPassCount()); assertEquals("smoke test fail count", 1, smoke.getFailCount()); TestResult firstSmokeChild = smoke.getChildByIndex(0); assertEquals("expect 1 failure", 1, firstSmokeChild.getFailCount()); // Can't test this method; it's unimplemented: assertEquals("expect 1 failure", 1, firstSmokeChild.getFailedTests().size()); assertEquals("exepct pass", 80, firstSmokeChild.getPassCount()); // Can't test this method; it's unimplemented: assertEquals("expect pass", 80, firstSmokeChild.getPassedTests().size()); // Drill down all the way into the TestResult (junit) assertTrue( "it should be a TestResult", firstSmokeChild instanceof TestResult ); hudson.tasks.junit.TestResult tr = (hudson.tasks.junit.TestResult) firstSmokeChild; PackageResult aPackage = tr.byPackage("hudson.matrix"); assertNotNull("should have a hudson.matrix package", aPackage); assertEquals("number of passed tests in hudson.matrix", 3, aPackage.getPassCount()); aPackage = tr.byPackage("hudson.security"); assertNotNull("should have a hudson.security package", aPackage); assertEquals("number of failed tests in hudson.security", 1, aPackage.getFailCount()); assertEquals("number of passed tests in hudson.security", 1, aPackage.getPassCount()); // TODO: re-check all the above data with a webclient. // TODO: check the xml api } @LocalData public void testJustJavaGrouping() throws Exception { buildJavaGroupingProjectAndValidate(setupProject(JUST_JAVA_GROUPS)); } /** * Tests whether we can get result in previous builds. That behavior is not yet * supported in labeled test groups, so I'm deactivating this test. * @throws Exception */ @LocalData public void SKIP_testGetResultInBuild() throws Exception { int numberOfBuildsToRun = 5; assertTrue("gotta have enough builds", numberOfBuildsToRun > 3); FreeStyleProject proj = setupHistoryTest(numberOfBuildsToRun); FreeStyleBuild lastBuild = proj.getLastBuild(); FreeStyleBuild prevBuild = lastBuild.getPreviousBuild(); FreeStyleBuild firstBuild = proj.getFirstBuild(); MetaLabeledTestResultGroupAction action = lastBuild.getAction(MetaLabeledTestResultGroupAction.class); MetaLabeledTestResultGroup metaResult = action.getResultAsTestResultGroup(); assertMatchingResults("metaResult in lastBuild", metaResult, metaResult.getResultInBuild(lastBuild)); assertMatchingResults("metaResult in prevBuild", metaResult, metaResult.getResultInBuild(prevBuild)); assertMatchingResults("metaResult in firstBuild", metaResult, metaResult.getResultInBuild(firstBuild)); for (String l : metaResult.getLabels()) { LabeledTestResultGroup group = metaResult.getGroupByLabel(l); TestResult groupInFirstBuild = group.getResultInBuild(firstBuild); TestResult groupInLastBuild = group.getResultInBuild(lastBuild); TestResult groupinPrevBuild = group.getResultInBuild(prevBuild); assertMatchingResults("group in firstBuild", group, groupInFirstBuild); assertMatchingResults("group in lastBuild", group, groupInLastBuild); assertMatchingResults("group in prevBuild", group, groupinPrevBuild); // TODO: Drill even farther down } } @LocalData public void testRemoteJavaGrouping() throws Exception { FreeStyleProject freestyleProj = setupProject(JUST_JAVA_GROUPS); DumbSlave s = createOnlineSlave(); freestyleProj.setAssignedLabel(s.getSelfLabel()); setupRemoteData(JUST_JAVA_GROUPS, s, freestyleProj, "*.xml"); buildJavaGroupingProjectAndValidate(freestyleProj); } public FreeStyleProject setupHistoryTest(int numberOfBuildsToRun) throws Exception { FreeStyleProject proj = setupProject(JUST_JAVA_GROUPS); // build several times List<FreeStyleBuild> builds = new ArrayList<FreeStyleBuild>(numberOfBuildsToRun); for (int i = 0; i < numberOfBuildsToRun; i++) { FreeStyleBuild build = proj.scheduleBuild2(0).get(10, TimeUnit.SECONDS); // leave time for interactive debugging builds.add(build); } return proj; } @LocalData public void testHistory() throws Exception { FreeStyleProject proj = setupHistoryTest(3); String[] relativePaths = { "testReport/history/", "testReport/smoke/history/", /* We know this will fail "testReport/integration/integration-hudson.tasks.junit.JUnitParser/history/", */ }; // Go to the history page HudsonTestCase.WebClient wc = new HudsonTestCase.WebClient(); for (String relativeUrl : relativePaths) { HtmlPage page = wc.goTo(proj.getLastBuild().getUrl() + relativeUrl ); assertGoodHistoryPage(page); } // then visit http://localhost:8080/job/just_java_groups/3/testReport/integration/history/ // and http://localhost:8080/job/just_java_groups/3/testReport/integration/integration-hudson.tasks.junit.JUnitParser/history/ // it should have some useful data, and it should not say "More than 1 builds are needed for the chart." // Go here: // http://localhost:8080/job/just_java_groups/3/testReport/regression/ // we should have a good previous result } private void assertGoodHistoryPage(HtmlPage page) { String uri = page.getDocumentURI(); assertTrue("good http status for " + uri, isGoodHttpStatus(page.getWebResponse().getStatusCode())); // The page should not say "More than 1 builds are needed for the chart." final String NO_HISTORY_CHART_MSG = "More than 1 builds are needed for the chart."; final String PROGRAMMING_ERROR_MSG = "programming error"; String pageText = page.asText(); assertFalse("should not say 'more than 1 builds are needed for the chart on page '" + uri, pageText.contains(NO_HISTORY_CHART_MSG)); assertFalse("should not say 'programming error' on page" + uri, pageText.contains(PROGRAMMING_ERROR_MSG)); HtmlElement wholeTable = page.getElementById("testresult"); assertNotNull("table with id 'testresult' exists on page " + uri, wholeTable); assertTrue("wholeTable is a table on page " + uri, wholeTable instanceof HtmlTable); HtmlTable table = (HtmlTable) wholeTable; // We really want to call table.getRowCount(), but // it returns 1, not the real answer, // because this table has *two* tbody elements, // and getRowCount() only seems to count the *first* tbody. // Maybe HtmlUnit can't handle the two tbody's. In any case, // the tableText.contains tests do a (ahem) passable job // of detecting whether the history results are present. String tableText = table.getTextContent(); assertTrue("table text should have header that says Fail on page " + uri, tableText.contains("Fail")); assertTrue("table text should have header that says Skip on page " + uri, tableText.contains("Skip")); // assert that there is a table with some interesting history in it assertTrue("table text content should have the project name in it on page " + uri, tableText.contains(JUST_JAVA_GROUPS)); assertTrue("table text content should have some build numbers in it on page " + uri, tableText.contains("#1")); } @LocalData public void testPromotedFailureLinks() throws Exception, InterruptedException { // We should have a list of links to failures. // Those links should be traversible. FreeStyleProject freestyleProj = setupProject(CAT_PROJECT_NAME); FreeStyleBuild build = freestyleProj.scheduleBuild2(0).get(10, TimeUnit.SECONDS); HudsonTestCase.WebClient wc = new HudsonTestCase.WebClient(); HtmlPage failingTestPage = wc.goTo(freestyleProj.getLastBuild().getUrl() + "testReport/special/junit/tacoshack.meals/NachosTest/testBeanDip/"); assertXPath(failingTestPage, "//h1[@class='result-failed']" ); String testReportPageUrl = freestyleProj.getLastBuild().getUrl() + "/testReport/special/junit/"; HtmlPage testReportPage = wc.goTo( testReportPageUrl ); Page packagePage = testReportPage.getFirstAnchorByText("tacoshack.meals").click(); assertGoodStatus(packagePage); // I expect this to work; just checking that my use of the APIs is correct. // Now we're on that page. We should be able to find a link to the failed test in there. HtmlAnchor anchor = testReportPage.getFirstAnchorByText("tacoshack.meals.NachosTest.testBeanDip"); String href = anchor.getHrefAttribute(); System.out.println("link is : " + href); Page failureFromLink = anchor.click(); assertGoodStatus(failureFromLink); } /** * This test is all about tally(). * * @throws Exception */ @LocalData public void testPersistence() throws Exception { FreeStyleProject project = setupProject(JUST_JAVA_GROUPS); FreeStyleBuild buildBeforeShutdown = project.scheduleBuild2(0).get(60, TimeUnit.SECONDS); assertJavaProjectTestResults(buildBeforeShutdown); reloadHudson(); FreeStyleProject projectAfterReload = (FreeStyleProject) hudson.getItem(JUST_JAVA_GROUPS); FreeStyleBuild build = projectAfterReload.getBuildByNumber(1); assertJavaProjectTestResults(build); } /** * This started failing when we merged in 1.343 from upstream, because Hudson.load() * doesn't seem to be an API anymore. * @throws NoSuchMethodException * @throws IllegalAccessException * @throws InvocationTargetException */ private void reloadHudson() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method m = Hudson.class.getDeclaredMethod("loadTasks"); m.setAccessible(true); m.invoke(hudson); } /** * Test that we can switch the graph on the project summary page to * just show failures. * @throws Exception */ @LocalData public void testJustShowFailures() throws Exception { FreeStyleProject proj = setupHistoryTest(4); assertNotNull("our project should exist", proj); HudsonTestCase.WebClient wc = new HudsonTestCase.WebClient(); HtmlPage projectPage = wc.goTo(proj.getUrl()); String pageContent = projectPage.asXml(); String XPATH_TO_LINK = "//a[@id='change-mode-link']"; Object o = projectPage.getDocumentElement().selectSingleNode(XPATH_TO_LINK); assertNotNull("found the link", o); assertTrue("the link is a node", o instanceof org.w3c.dom.Node); HtmlAnchor anchor = projectPage.getAnchorByName("change-mode-link"); assertNotNull("found the anchor", anchor); Page afterClick = anchor.click(); assertGoodStatus(afterClick); // TODO: verify that the image we get back has the expected data. (I've verified this interactively.) // TODO: verify that we set a cookie correctly. I'm not sure if the WebClient handles cookies correctly. } }