view mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java @ 732:31a1aca3305c

Update copyright
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Tue, 14 Jan 2014 12:45:10 +0100
parents 1da36037196d
children 83e1fb5aa843
line wrap: on
line source
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;

import com.intellij.openapi.diagnostic.Logger;
import jetbrains.buildServer.ExecResult;
import jetbrains.buildServer.StreamGobbler;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.AbandonedTransactionFound;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownRevisionException;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnrelatedRepositoryException;
import jetbrains.buildServer.vcs.VcsException;
import org.apache.log4j.Level;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.testng.AssertJUnit.*;
import static org.testng.AssertJUnit.assertTrue;

/**
 * @author dmitry.neverov
 */
@Test
public class CommandResultTest {

  private RecordingLogger myLogger;

  @BeforeMethod
  public void setUp() {
    myLogger = new RecordingLogger();
  }

  public void output_should_not_contain_private_data() {
    String password = "pass";
    CommandResult commandResult = commandResultFor(execResult().withStdout(password).withStderr(password), password);
    assertFalse(commandResult.getStdout().contains(password));
    myLogger.assertLogMessagesDontContain(password);
  }

  @DataProvider(name = "exitCodesForErrors")
  public static Object[][] exitCodesForErrors() {
    return new Object[][] {
            new Object[] { -1 },
            new Object[] { 255}};
  }

  @Test(expectedExceptions = VcsException.class,
        dataProvider = "exitCodesForErrors")
  public void should_detect_error_for_exit_code(int exitCode) throws VcsException {
    CommandResult commandResult = commandResultFor(execResult().withExitCode(exitCode));
    commandResult.checkCommandFailed();
  }

  public void exit_code_of_one_is_not_an_error() throws VcsException {
    CommandResult commandResult = commandResultFor(execResult().withStdout("pull: no new changes").withExitCode(1));
    commandResult.checkCommandFailed();
  }

  @Test(expectedExceptions = UnrelatedRepositoryException.class)
  public void should_detect_unrelated_repository_error() throws VcsException {
    String unrelatedRepositoryStderr = "abort: repository is unrelated\n";
    CommandResult commandResult = commandResultFor(execResult().withStderr(unrelatedRepositoryStderr).withExitCode(255));
    commandResult.checkCommandFailed();
  }

  @Test(expectedExceptions = AbandonedTransactionFound.class)
  public void should_detect_abandoned_transaction_error() throws VcsException {
    String abandonedTransactionError = "abort: abandoned transaction found - run hg recover!\n";
    CommandResult commandResult = commandResultFor(execResult().withStderr(abandonedTransactionError).withExitCode(255));
    commandResult.checkCommandFailed();
  }

  @Test
  public void should_detect_unknown_revision_error() throws VcsException {
    String unknownRevision = "9c6a6b4aede0";
    String unknownRevisionError = "abort: unknown revision '" + unknownRevision + "'\n";
    CommandResult commandResult = commandResultFor(execResult().withStderr(unknownRevisionError).withExitCode(255));
    try {
      commandResult.checkCommandFailed();
      fail("unknown exception should be thrown");
    } catch (UnknownRevisionException e) {
      assertEquals(unknownRevision, e.getRevision());
    }
  }

  @Test(expectedExceptions = VcsException.class)
  public void should_detect_failure_when_delegate_has_exception() throws VcsException {
    CommandResult commandResult = commandResultFor(execResult().withException(new RuntimeException()));
    commandResult.checkCommandFailed();
  }

  public void exception_should_not_contain_command_stdout_or_stderr() {
    final String stdout = "300Mb of output";
    final String stderr = "300Mb from stderr";
    CommandSettings settings = new CommandSettings();
    settings.setExceptionOutputLimit(0);
    CommandResult cr = commandResultFor(execResult().withStdout(stdout).withStderr(stderr), settings);
    try {
      cr.checkCommandFailed();
    } catch (VcsException e) {
      assertThat(e.getMessage(), not(containsString(stdout)));
      assertThat(e.getMessage(), not(containsString(stderr)));
    }

    settings = new CommandSettings();
    settings.setExceptionOutputLimit(14);
    cr = commandResultFor(execResult().withStdout(stdout).withStderr(stderr), settings);
    try {
      cr.checkCommandFailed();
    } catch (VcsException e) {
      assertThat(e.getMessage(), containsString("300Mb from..."));
      assertThat(e.getMessage(), containsString("See details in teamcity-vcs.log"));
    }
  }

  public void when_command_fails_stderr_should_be_logged() {
    CommandResult cr = commandResultFor(execResult().withStderr("an important error").withExitCode(255));
    try {
      cr.checkCommandFailed();
    } catch (VcsException e) {
      //ignore
    }
    assertTrue(myLogger.contains("an important error"));
  }

  ExecResultBuilder execResult() {
    return new ExecResultBuilder();
  }

  CommandResult commandResultFor(ExecResultBuilder builder, String... privateData) {
    return new CommandResult(myLogger, "", builder.build(), new HashSet<String>(Arrays.asList(privateData)), new CommandSettings());
  }

  CommandResult commandResultFor(ExecResultBuilder builder, CommandSettings settings) {
    return new CommandResult(myLogger, "", builder.build(), new HashSet<String>(), settings);
  }

  private class ExecResultBuilder {
    private String myStdout = "";
    private String myStderr = "";
    private Throwable myException = null;
    private int myExitCode = 0;

    ExecResultBuilder withStdout(String stdout) {
      myStdout = stdout;
      return this;
    }
    ExecResultBuilder withStderr(String stderr) {
      myStderr = stderr;
      return this;
    }
    ExecResultBuilder withException(Throwable exception) {
      myException = exception;
      return this;
    }
    ExecResultBuilder withExitCode(int exitCode) {
      myExitCode = exitCode;
      return this;
    }

    ExecResult build() {
      ExecResult result = new ExecResult();
      result.setOutputGobbler(createStringGobbler(myStdout));
      result.setErrorGobbler(createStringGobbler(myStderr));
      if (myException != null)
        result.setException(myException);
      if (myExitCode != 0)
        result.setExitCode(myExitCode);
      return result;
    }

    private StreamGobbler createStringGobbler(@NotNull final String str) {
      StreamGobbler gobbler = new StreamGobbler(new ByteArrayInputStream(str.getBytes()));
      gobbler.start();
      try {
        Thread.sleep(1000);//wait for gobbler to read string
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
      return gobbler;
    }
  }

  private class RecordingLogger extends Logger {

    private List<String> myMessages = new ArrayList<String>();

    public boolean contains(@NotNull String entry) {
      for (String message : myMessages) {
        if (message.contains(entry))
          return true;
      }
      return false;
    }

    public void assertLogMessagesDontContain(@NotNull String... strs) {
      for (String s : strs) {
        for (String message : myMessages)
          assertFalse("'" + s + "' was logged", message.contains(s));
      }
    }

    @Override
    public boolean isDebugEnabled() {
      return true;
    }

    @Override
    public void debug(@NonNls String s) {
      myMessages.add(s);
    }

    @Override
    public void debug(Throwable throwable) {
      myMessages.add(throwable.getMessage());
    }

    @Override
    public void debug(@NonNls String s, Throwable throwable) {
      myMessages.add(s);
    }

    @Override
    public void error(@NonNls String s, Throwable throwable, @NonNls String... strings) {
      myMessages.add(s);
    }

    @Override
    public void info(@NonNls String s) {
      myMessages.add(s);
    }

    @Override
    public void info(@NonNls String s, Throwable throwable) {
      myMessages.add(s);
    }

    @Override
    public void warn(@NonNls String s, Throwable throwable) {
      myMessages.add(s);
    }

    @Override
    public void setLevel(Level level) {
    }
  }
}