All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fitnesse.responders.run.SuiteResponder Maven / Gradle / Ivy

There is a newer version: 20181217
Show newest version
// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.responders.run;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import fitnesse.FitNesseContext;
import fitnesse.authentication.SecureOperation;
import fitnesse.authentication.SecureResponder;
import fitnesse.authentication.SecureTestOperation;
import fitnesse.components.TraversalListener;
import fitnesse.html.template.HtmlPage;
import fitnesse.html.template.PageTitle;
import fitnesse.http.Request;
import fitnesse.http.Response;
import fitnesse.reporting.BaseFormatter;
import fitnesse.reporting.Formatter;
import fitnesse.reporting.InteractiveFormatter;
import fitnesse.reporting.SuiteHtmlFormatter;
import fitnesse.reporting.TestTextFormatter;
import fitnesse.reporting.history.HistoryPurger;
import fitnesse.reporting.history.JunitReFormatter;
import fitnesse.reporting.history.PageHistory;
import fitnesse.reporting.history.SuiteHistoryFormatter;
import fitnesse.reporting.history.SuiteXmlReformatter;
import fitnesse.reporting.history.TestXmlFormatter;
import fitnesse.responders.ChunkingResponder;
import fitnesse.responders.WikiImporter;
import fitnesse.responders.WikiImportingResponder;
import fitnesse.responders.WikiImportingTraverser;
import fitnesse.responders.WikiPageActions;
import fitnesse.testrunner.MultipleTestsRunner;
import fitnesse.testrunner.PagesByTestSystem;
import fitnesse.testrunner.RunningTestingTracker;
import fitnesse.testrunner.SuiteContentsFinder;
import fitnesse.testrunner.SuiteFilter;
import fitnesse.testsystems.ConsoleExecutionLogListener;
import fitnesse.testsystems.ExecutionLogListener;
import fitnesse.testsystems.TestExecutionException;
import fitnesse.testsystems.TestSummary;
import fitnesse.wiki.PageCrawler;
import fitnesse.wiki.PageData;
import fitnesse.wiki.PageType;
import fitnesse.wiki.PathParser;
import fitnesse.wiki.WikiImportProperty;
import fitnesse.wiki.WikiPage;
import fitnesse.wiki.WikiPagePath;
import fitnesse.wiki.WikiPageUtil;
import org.apache.commons.lang.StringUtils;
import util.FileUtil;

import static fitnesse.responders.WikiImportingTraverser.ImportError;
import static fitnesse.wiki.WikiImportProperty.isAutoUpdated;

public class SuiteResponder extends ChunkingResponder implements SecureResponder {
  private static final Logger LOG = Logger.getLogger(SuiteResponder.class.getName());

  private static final String NOT_FILTER_ARG = "excludeSuiteFilter";
  private static final String AND_FILTER_ARG = "runTestsMatchingAllTags";
  private static final String OR_FILTER_ARG_1 = "runTestsMatchingAnyTag";
  private static final String OR_FILTER_ARG_2 = "suiteFilter";

  static final RunningTestingTracker runningTestingTracker = new RunningTestingTracker();

  private final WikiImporter wikiImporter;
  private SuiteHistoryFormatter suiteHistoryFormatter;

  private PageData data;
  private String testRunId;
  private BaseFormatter mainFormatter;
  private volatile boolean isClosed = false;

  private boolean debug = false;
  private boolean remoteDebug = false;
  private boolean includeHtml = false;
  private int exitCode;

  public SuiteResponder() {
    this(new WikiImporter());
  }

  public SuiteResponder(WikiImporter wikiImporter) {
    super();
    this.wikiImporter = wikiImporter;
  }

  private boolean isInteractive() {
    return mainFormatter instanceof InteractiveFormatter;
  }

  @Override
  public Response makeResponse(FitNesseContext context, Request request) throws Exception {
    Response result = super.makeResponse(context, request);
    if (result != response){
        return result;
    }
    testRunId = runningTestingTracker.generateNextTicket();
    response.addHeader("X-FitNesse-Test-Id", testRunId);
    return response;
  }

  @Override
  protected void doSending() throws Exception {
    debug |= request.hasInput("debug");
    remoteDebug |= request.hasInput("remote_debug");
    includeHtml |= request.hasInput("includehtml");
    data = page.getData();

    createMainFormatter();

    if (isInteractive()) {
      makeHtml().render(response.getWriter());
    } else {
      doExecuteTests();
    }

    closeHtmlResponse(exitCode);

    cleanHistoryForSuite();
  }

  private void cleanHistoryForSuite() {
    String testHistoryDays = context.getProperty("test.history.days");
    if (withSuiteHistoryFormatter() && StringUtils.isNumeric(testHistoryDays)) {
      new HistoryPurger(context.getTestHistoryDirectory(), Integer.parseInt(testHistoryDays))
              .deleteTestHistoryOlderThanDays(path);
    }
  }

  public void doExecuteTests() {
    if (WikiImportProperty.isImported(data)) {
      importWikiPages();
    }

    try {
      performExecution();
    } catch (Exception e) {
      LOG.log(Level.INFO, "Test system terminated with exception", e);
    }

    exitCode = mainFormatter.getErrorCount();
  }

  public void importWikiPages() {
    if (response.isXmlFormat() || !isAutoUpdated(data != null ? data : page.getData())) {
      return;
    }

    try {
      addToResponse("Updating imported content...");
      new WikiImportingTraverser(wikiImporter, page).traverse(new TraversalListener() {
        @Override
        public void process(Object pageOrError) {
          if (pageOrError instanceof ImportError) {
            ImportError error = (ImportError) pageOrError;
            addToResponse(" " + error.toString() + ".");
          }
        }
      });
      addToResponse(" Done.");
      // Refresh data, since it might have changed.
      data = page.getData();
    } catch (IOException e) {
      addToResponse(" Import failed: " + e.toString() + ".");
    } finally {
      addToResponse("");
    }
  }

  private HtmlPage makeHtml() {
    PageCrawler pageCrawler = page.getPageCrawler();
    WikiPagePath fullPath = pageCrawler.getFullPath();
    String fullPathName = PathParser.render(fullPath);
    HtmlPage htmlPage = context.pageFactory.newPage();
    htmlPage.setTitle(getTitle() + ": " + fullPathName);
    htmlPage.setPageTitle(new PageTitle(getTitle(), fullPath, data.getAttribute(PageData.PropertySUITES)));
    htmlPage.setNavTemplate("testNav.vm");
    htmlPage.put("actions", new WikiPageActions(page));
    htmlPage.setMainTemplate(mainTemplate());
    htmlPage.put("testExecutor", new TestExecutor());
    htmlPage.setFooterTemplate("wikiFooter.vm");
    htmlPage.put("headerContent", new WikiPageHeaderRenderer());
    htmlPage.put("footerContent", new WikiPageFooterRenderer());
    htmlPage.setErrorNavTemplate("errorNavigator");
    htmlPage.put("multipleTestsRun", isMultipleTestsRun());
    WikiImportingResponder.handleImportProperties(htmlPage, page);

    return htmlPage;
  }

  public boolean isDebug() {
    return debug;
  }

  public void setDebug(boolean debug) {
    this.debug = debug;
  }

  public class WikiPageHeaderRenderer {

    public String render() {
      return WikiPageUtil.getHeaderPageHtml(page);
    }

  }

  public class WikiPageFooterRenderer {

    public String render() {
        return WikiPageUtil.getFooterPageHtml(page);
    }

  }

  public class TestExecutor {
    public void execute() {
        doExecuteTests();
    }
  }

  private boolean isMultipleTestsRun() {
    return PageType.fromWikiPage(page) == PageType.SUITE;
  }

  protected void addFormatters(MultipleTestsRunner runner) {
    runner.addTestSystemListener(mainFormatter);
    if (withSuiteHistoryFormatter()) {
      addHistoryFormatter(runner);
    } else {
      runner.addExecutionLogListener(new ConsoleExecutionLogListener());
    }
    if (mainFormatter instanceof ExecutionLogListener) {
      runner.addExecutionLogListener((ExecutionLogListener) mainFormatter);
    }
    for (Formatter formatter : context.formatterFactory.createFormatters()) {
      runner.addTestSystemListener(formatter);
    }
    if (context.testSystemListener != null) {
      runner.addTestSystemListener(context.testSystemListener);
    }
  }

  private boolean withSuiteHistoryFormatter() {
    return !request.hasInput("nohistory");
  }

  protected void addHistoryFormatter(MultipleTestsRunner runner) {
    SuiteHistoryFormatter historyFormatter = getSuiteHistoryFormatter();
    runner.addTestSystemListener(historyFormatter);
    runner.addExecutionLogListener(historyFormatter);
  }

  private void createMainFormatter() {
    if (response.isXmlFormat()) {
      mainFormatter = newXmlFormatter();
    } else if (response.isTextFormat()) {
      mainFormatter = newTextFormatter();
    } else if (response.isJunitFormat()) {
      mainFormatter = newJunitFormatter();
    } else {
      mainFormatter = newHtmlFormatter();
    }
  }

  protected String getTitle() {
    return "Test Results";
  }

  protected String mainTemplate() {
    return "testPage";
  }

  protected BaseFormatter newXmlFormatter() {
    SuiteXmlReformatter xmlFormatter = new SuiteXmlReformatter(context, page, response.getWriter(), getSuiteHistoryFormatter());
    if (includeHtml)
      xmlFormatter.includeHtml();
    if (!isMultipleTestsRun())
      xmlFormatter.includeInstructions();
    return xmlFormatter;
  }

  protected BaseFormatter newTextFormatter() {
    return new TestTextFormatter(response);
  }

  protected BaseFormatter newJunitFormatter() {
    return new JunitReFormatter(context, page, response.getWriter(), getSuiteHistoryFormatter());
  }

  protected BaseFormatter newHtmlFormatter() {
    return new SuiteHtmlFormatter(page, response.getWriter());
  }

  protected void performExecution() throws TestExecutionException {
    MultipleTestsRunner runner = newMultipleTestsRunner(getPagesToRun());
    runningTestingTracker.addStartedProcess(testRunId, runner);
    if (isInteractive()) {
      ((InteractiveFormatter) mainFormatter).setTrackingId(testRunId);
    }
    try {
      runner.executeTestPages();
    } finally {
      runningTestingTracker.removeEndedProcess(testRunId);
    }
  }

  protected List getPagesToRun() {
    SuiteFilter filter = createSuiteFilter(request, page.getPageCrawler().getFullPath().toString());
    SuiteContentsFinder suiteTestFinder = new SuiteContentsFinder(page, filter, root);
    return suiteTestFinder.getAllPagesToRunForThisSuite();
  }

  protected MultipleTestsRunner newMultipleTestsRunner(List pages) {
    // Add test url inputs to context's variableSource.
    final PagesByTestSystem pagesByTestSystem = new PagesByTestSystem(pages, root);

    MultipleTestsRunner runner = new MultipleTestsRunner(pagesByTestSystem, context.testSystemFactory);
    runner.setRunInProcess(debug);
    runner.setEnableRemoteDebug(remoteDebug);
    addFormatters(runner);

    return runner;
  }

  @Override
  public SecureOperation getSecureOperation() {
    return new SecureTestOperation();
  }

  public void addToResponse(String output) {
    if (!isClosed()) {
      try {
        response.add(output);
      } catch (IOException e) {
        LOG.log(Level.WARNING, "Unable to send output", e);
      }
    }
  }

  synchronized boolean isClosed() {
    return isClosed;
  }

  synchronized void setClosed() {
    isClosed = true;
  }

  void closeHtmlResponse(int exitCode) throws IOException {
    if (!isClosed()) {
      setClosed();
      response.closeChunks();
      response.addTrailingHeader("Exit-Code", String.valueOf(exitCode));
      response.closeTrailer();
      response.close();
    }
  }

  public Response getResponse() {
    return response;
  }


  public static SuiteFilter createSuiteFilter(Request request, String suitePath) {
    return new SuiteFilter(getOrTagFilter(request),
            getNotSuiteFilter(request),
            getAndTagFilters(request),
            getSuiteFirstTest(request, suitePath));
  }

  private static String getOrTagFilter(Request request) {
    return request != null ? getOrFilterString(request) : null;
  }

  private static String getOrFilterString(Request request) {
    //request already confirmed not-null
    String orFilterString = null;
    if(request.getInput(OR_FILTER_ARG_1) != null){
      orFilterString = request.getInput(OR_FILTER_ARG_1);
    } else {
      orFilterString = request.getInput(OR_FILTER_ARG_2);
    }
    return orFilterString;
  }

  private static String getNotSuiteFilter(Request request) {
    return request != null ? request.getInput(NOT_FILTER_ARG) : null;
  }

  private static String getAndTagFilters(Request request) {
    return request != null ? request.getInput(AND_FILTER_ARG) : null;
  }


  private static String getSuiteFirstTest(Request request, String suiteName) {
    String startTest = null;
    if (request != null) {
      startTest = request.getInput("firstTest");
    }

    if (startTest != null) {
      if (startTest.indexOf(suiteName) != 0) {
        startTest = suiteName + "." + startTest;
      }
    }

    return startTest;
  }


  public static class HistoryWriterFactory implements TestXmlFormatter.WriterFactory {

    @Override
    public Writer getWriter(FitNesseContext context, WikiPage page, TestSummary counts, long time) throws IOException {
      File resultPath = new File(makePageHistoryFileName(context, page, counts, time));
      File resultDirectory = new File(resultPath.getParent());
      if (!resultDirectory.exists()) {
        resultDirectory.mkdirs();
      }
      File resultFile = new File(resultDirectory, resultPath.getName());
      return new PrintWriter(resultFile, FileUtil.CHARENCODING);
    }
  }

  public static String makePageHistoryFileName(FitNesseContext context, WikiPage page, TestSummary counts, long time) {
    return String.format("%s/%s/%s",
            context.getTestHistoryDirectory(),
            page.getPageCrawler().getFullPath().toString(),
            makeResultFileName(counts, time));
  }

  public static String makeResultFileName(TestSummary summary, long time) {
    SimpleDateFormat format = new SimpleDateFormat(PageHistory.TEST_RESULT_FILE_DATE_PATTERN);
    String datePart = format.format(new Date(time));
    return String.format("%s_%d_%d_%d_%d.xml", datePart, summary.getRight(), summary.getWrong(), summary.getIgnores(), summary.getExceptions());
  }

  private SuiteHistoryFormatter getSuiteHistoryFormatter() {
    if (suiteHistoryFormatter == null) {
      HistoryWriterFactory source = new HistoryWriterFactory();
      suiteHistoryFormatter = new SuiteHistoryFormatter(context, page, source);
    }
    return suiteHistoryFormatter;
  }
}