/**
   * Tests {@link
   * TriggerContextConverter#unmarshal(com.thoughtworks.xstream.io.HierarchicalStreamReader,
   * com.thoughtworks.xstream.converters.UnmarshallingContext)}. With "matrix_build.xml" as input.
   *
   * @throws Exception if so.
   */
  @Test
  public void testUnmarshalOldMatrixBuild() throws Exception {
    XStream xStream = new XStream2();
    xStream.registerConverter(new TriggerContextConverter());
    xStream.alias("matrix-run", MatrixRun.class);
    Object obj = xStream.fromXML(getClass().getResourceAsStream("matrix_build.xml"));
    assertTrue(obj instanceof MatrixRun);
    MatrixRun run = (MatrixRun) obj;

    Cause.UpstreamCause upCause = run.getCause(Cause.UpstreamCause.class);
    List upstreamCauses = Whitebox.getInternalState(upCause, "upstreamCauses");
    GerritCause cause = (GerritCause) upstreamCauses.get(0);
    assertNotNull(cause.getEvent());
    assertEquals("platform/project", cause.getEvent().getChange().getProject());
    assertNotNull(cause.getContext());
    assertNotNull(cause.getContext().getThisBuild());

    assertEquals("Gerrit_master-theme_matrix", cause.getContext().getThisBuild().getProjectId());
    assertEquals(102, cause.getContext().getThisBuild().getBuildNumber().intValue());

    assertNotNull(cause.getContext().getOthers());
    assertEquals(1, cause.getContext().getOthers().size());

    TriggeredItemEntity entity = cause.getContext().getOthers().get(0);
    assertEquals("master-theme", entity.getProjectId());
    assertNull(entity.getBuildNumber());
  }
  /**
   * Determines if the given configuration is currently running. If any of the configurations are
   * currently stuck in the queue, it is logged.
   *
   * @param execution Contains information about the general build, including the listener used to
   *     log queue blockage.
   * @param configuration The configuration being checked to see if it's running.
   * @param mutableWhyMap Mutable map used to track the reasons a configuration is stuck in the
   *     queue. This prevents duplicate reasons from flooding the logs.
   * @return True if the build represented by the given configuration is currently running or stuck
   *     in the queue. False if the build has finished running.
   */
  private boolean isBuilding(
      MatrixBuild.MatrixBuildExecution execution,
      MatrixConfiguration configuration,
      Map<String, String> mutableWhyMap) {
    MatrixRun build = configuration.getBuildByNumber(execution.getBuild().getNumber());
    if (build != null) {
      return build.isBuilding();
    }

    Queue.Item queueItem = configuration.getQueueItem();
    if (queueItem != null) {
      String why = queueItem.getWhy();
      String key = queueItem.task.getFullDisplayName() + " " + queueItem.id;
      String oldWhy = mutableWhyMap.get(key);
      if (why == null) {
        mutableWhyMap.remove(key);
      }
      if (why != null && !why.equals(oldWhy)) {
        mutableWhyMap.put(key, why);
        BuildListener listener = execution.getListener();
        PrintStream logger = listener.getLogger();
        logger.print(
            "Configuration "
                + ModelHyperlinkNote.encodeTo(configuration)
                + " is still in the queue: ");
        queueItem.getCauseOfBlockage().print(listener); // this is still shown on the same line
      }
    }

    return true;
  }
 public void testNoInterpreter() throws Exception {
   ToxBuilder builder = new ToxBuilder("tox.ini", false, null);
   MatrixProject project = createMatrixProject();
   AxisList axes = new AxisList(new ToxAxis(new String[] {"py27"}));
   project.setAxes(axes);
   project.getBuildersList().add(builder);
   MatrixBuild build = project.scheduleBuild2(0).get();
   List<MatrixRun> runs = build.getRuns();
   assertEquals(1, runs.size());
   MatrixRun run = runs.get(0);
   String log = FileUtils.readFileToString(run.getLogFile());
   assertTrue(
       "should not have found an interpreter:\n" + log,
       log.contains(Messages.BuilderUtil_NoInterpreterFound()));
 }
 public void testNoAxis() throws Exception {
   ToxBuilder builder = new ToxBuilder("tox.ini", false, null);
   MatrixProject project = createMatrixProject();
   AxisList axes = new AxisList(new TextAxis("TOTO", "TUTU"));
   project.setAxes(axes);
   project.getBuildersList().add(builder);
   MatrixBuild build = project.scheduleBuild2(0).get();
   List<MatrixRun> runs = build.getRuns();
   assertEquals(1, runs.size());
   MatrixRun run = runs.get(0);
   String log = FileUtils.readFileToString(run.getLogFile());
   assertTrue(
       "should not have found a tox axis:\n" + log,
       log.contains(Messages.ToxBuilder_ToxAxis_Required()));
 }
 public void testToxAxisSuccessful() throws Exception {
   configureCPython2();
   ToxBuilder builder = new ToxBuilder("tox.ini", false, null);
   MatrixProject project = createMatrixProject();
   AxisList axes = new AxisList(new ToxAxis(new String[] {"py27"}));
   project.setScm(new ToxSCM("tox.ini", "[testenv]\ncommand = echo"));
   project.setAxes(axes);
   project.getBuildersList().add(builder);
   MatrixBuild build = project.scheduleBuild2(0).get();
   List<MatrixRun> runs = build.getRuns();
   assertEquals(1, runs.size());
   MatrixRun run = runs.get(0);
   String log = FileUtils.readFileToString(run.getLogFile());
   assertTrue("tox should have been successful:\n" + log, log.contains("congratulations :)"));
   assertTrue("build should have been successful:\n" + log, log.contains("SUCCESS"));
 }
  @Test
  public void shortenMatrix() throws Exception {
    Node slave = j.createOnlineSlave();
    setMaxPathLength(slave, 1); // Not enough for anything

    MatrixProject mp = j.createMatrixProject();
    mp.setAssignedNode(slave);
    mp.setAxes(new AxisList(new LabelExpAxis("axis", slave.getNodeName())));

    MatrixBuild build = j.buildAndAssertSuccess(mp);
    assertThat(build.getBuiltOn(), equalTo(slave));
    MatrixRun run = build.getExactRuns().get(0);
    assertThat(run.getBuiltOn(), equalTo(slave));

    System.out.println(build.getWorkspace());
    System.out.println(run.getWorkspace());
  }
 public void testToxenvPatternBlank() throws Exception {
   configureCPython2();
   ToxBuilder builder = new ToxBuilder("tox.ini", false, "$FOOBAR");
   MatrixProject project = createMatrixProject();
   AxisList axes = new AxisList(new TextAxis("FOOBAR2", "badluck"));
   project.setScm(new ToxSCM("tox.ini", "[testenv]\ncommand = echo"));
   project.setAxes(axes);
   project.getBuildersList().add(builder);
   MatrixBuild build = project.scheduleBuild2(0).get();
   List<MatrixRun> runs = build.getRuns();
   assertEquals(1, runs.size());
   MatrixRun run = runs.get(0);
   String log = FileUtils.readFileToString(run.getLogFile());
   System.out.println(log);
   assertTrue(
       "should not be able to run with a blank TOXENV pattern:\n" + log,
       log.contains(Messages.ToxBuilder_ToxenvPattern_Invalid("$FOOBAR")));
 }
 public void testToxAxisAndToxenvPattern() throws Exception {
   configureCPython2();
   ToxBuilder builder = new ToxBuilder("tox.ini", false, "$INTERPRETER$VERSION");
   MatrixProject project = createMatrixProject();
   AxisList axes =
       new AxisList(
           new ToxAxis(new String[] {"py27"}),
           new TextAxis("INTERPRETER", "py"),
           new TextAxis("VERSION", "27"));
   project.setScm(new ToxSCM("tox.ini", "[testenv]\ncommand = echo"));
   project.setAxes(axes);
   project.getBuildersList().add(builder);
   MatrixBuild build = project.scheduleBuild2(0).get();
   List<MatrixRun> runs = build.getRuns();
   assertEquals(1, runs.size());
   MatrixRun run = runs.get(0);
   String log = FileUtils.readFileToString(run.getLogFile());
   System.out.println(log);
   assertTrue(
       "should not be able to run with both Tox axis and TOXENV pattern:\n" + log,
       log.contains(Messages.ToxBuilder_ToxAxis_And_ToxenvPattern()));
 }
  /**
   * Checks if the logs of the given run match any of the given patterns, line-by-line.
   *
   * @param matrixRun The run to be considered.
   * @param patterns The patterns to match with.
   * @return True if at least one line of the logs match at least one of the given patterns.
   * @throws IOException If there's a problem reading the log file.
   */
  private boolean matchesPattern(MatrixRun matrixRun, List<Pattern> patterns) throws IOException {
    if (patterns == null || patterns.isEmpty()) {
      return true; // No specific patterns specified. Accept everything.
    }

    BufferedReader reader =
        new BufferedReader(
            new InputStreamReader(
                new FileInputStream(matrixRun.getLogFile()), matrixRun.getCharset()));
    try {
      for (String line = reader.readLine(); line != null; line = reader.readLine()) {
        for (Pattern pattern : patterns) {
          Matcher matcher = pattern.matcher(line);
          if (matcher.find()) {
            return true;
          }
        }
      }
    } finally {
      reader.close();
    }
    return false;
  }
  /**
   * Waits for the given configurations to finish, retrying any that qualify to be rerun.
   *
   * @param execution Provided by the plugin.
   * @param patterns List of regular expression patterns used to scan the log to determine if a
   *     build should be rerun.
   * @param retries Mutable map that tracks the number of times a specific configuration has been
   *     retried.
   * @param configurations The configurations that have already been scheduled to run that should be
   *     waited for to finish.
   * @return The worst result of all the runs. If a build was rerun, only the result of the rerun is
   *     considered.
   * @throws InterruptedException
   * @throws IOException
   */
  private Result waitForMatrixRuns(
      MatrixBuild.MatrixBuildExecution execution,
      List<Pattern> patterns,
      Map<MatrixConfiguration, Integer> retries,
      LinkedList<MatrixConfiguration> configurations)
      throws InterruptedException, IOException {
    BuildListener listener = execution.getListener();
    PrintStream logger = listener.getLogger();

    Map<String, String> whyBlockedMap =
        new HashMap<
            String,
            String>(); // keep track of why builds are blocked so we can print unique messages when
    // they change.
    Result finalResult = Result.SUCCESS;
    int iteration = 0;
    boolean continueRetrying = true;
    while (!configurations.isEmpty()) {
      ++iteration;
      MatrixConfiguration configuration = configurations.removeFirst();
      if (isBuilding(execution, configuration, whyBlockedMap)) {
        if (iteration >= configurations.size()) {
          // Every time we loop through all the configurations, sleep for a bit.
          // This is to prevent polling too often while everything is still building.
          iteration = 0;
          Thread.sleep(1000);
        }
        configurations.add(configuration);
        continue;
      }

      Run parentBuild = execution.getBuild();
      MatrixRun matrixRun = configuration.getBuildByNumber(parentBuild.getNumber());
      Result runResult = matrixRun.getResult();
      if (continueRetrying
          && runResult.isWorseOrEqualTo(getWorseThanOrEqualTo())
          && runResult.isBetterOrEqualTo(getBetterThanOrEqualTo())) {
        if (matchesPattern(matrixRun, patterns)) {
          int retriedCount = retries.get(configuration);
          if (retriedCount < getMaxRetries()) {
            ++retriedCount;
            retries.put(configuration, retriedCount);
            // rerun
            String logMessage =
                String.format(
                    "%s was %s. Matched pattern to rerun. Rerunning (%d).",
                    matrixRun, runResult, retriedCount);
            listener.error(logMessage);

            HealedAction action = parentBuild.getAction(HealedAction.class);
            if (action == null) {
              //noinspection SynchronizationOnLocalVariableOrMethodParameter
              synchronized (parentBuild.getActions()) {
                action = parentBuild.getAction(HealedAction.class);
                if (action == null) {
                  action = new HealedAction(matrixRun.getCharset());
                  parentBuild.addAction(action);
                }
              }
            }
            action.addAutoHealedJob(matrixRun);

            MatrixConfiguration parent = matrixRun.getParent();
            if (parent != null) {
              // I'm paranoid about NPEs
              parent.removeRun(matrixRun);
              matrixRun.delete();
            } else {
              LOGGER.severe(
                  "couldn't remove old run, parent was null. This is a Jenkins core bug.");
            }
            scheduleConfigurationBuild(
                execution, configuration, new SelfHealingCause(parentBuild, retriedCount));
            configurations.add(configuration);
            continue;
          } else {
            String logMessage =
                String.format(
                    "%s was %s. Matched pattern to rerun, but the max number of retries (%d) has been met.",
                    matrixRun, runResult, getMaxRetries());
            listener.error(logMessage);
            if (getStopRetryingAfterOneFails()) {
              listener.error("Not retrying any more builds.");
              continueRetrying = false;
            }
          }
        } else {
          String logMessage =
              String.format(
                  "%s was %s. It did not match the pattern to rerun. Accepting result.",
                  matrixRun, runResult);
          logger.println(logMessage);
        }
      }
      notifyEndRun(matrixRun, execution.getAggregators(), execution.getListener());
      finalResult = finalResult.combine(runResult);
    }
    return finalResult;
  }