TestProject.java

/*
 * @copyright defined in LICENSE.txt
 */

package ship.command;

import static hera.util.ObjectUtils.nvl;
import static java.util.Collections.EMPTY_LIST;
import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static ship.util.Messages.bind;

import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import lombok.Getter;
import lombok.Setter;
import ship.Builder;
import ship.BuilderFactory;
import ship.ProjectFile;
import ship.build.ResourceManager;
import ship.build.res.Project;
import ship.build.web.model.BuildDetails;
import ship.test.AthenaContext;
import ship.test.LuaErrorInformation;
import ship.test.LuaRunner;
import ship.test.LuaSource;
import ship.test.TestReportNode;
import ship.test.TestReportNodeResult;
import ship.test.TestResult;
import ship.test.TestResultCollector;

public class TestProject extends AbstractCommand {

  protected static final String NL_0 = TestProject.class.getName() + ".0";
  protected static final String NL_1 = TestProject.class.getName() + ".1";
  protected static final String NL_2 = TestProject.class.getName() + ".2";
  protected static final String NL_3 = TestProject.class.getName() + ".3";
  protected static final String NL_4 = TestProject.class.getName() + ".4";
  protected static final String NL_5 = TestProject.class.getName() + ".5";
  protected static final String NL_6 = TestProject.class.getName() + ".6";

  @Getter
  @Setter
  protected BuilderFactory builderFactory = project -> new Builder(new ResourceManager(project));

  @Setter
  protected Consumer<TestResultCollector> reporter = (testReporter) -> {
    testReporter.getResults().forEach(testFile -> {
      if (testFile.isSuccess()) {
        getPrinter().println(bind(NL_0, testFile.getName()));
      } else {
        final Optional<String> errorMessage =
            ofNullable(testFile.getResultDetail())
                .map(obj -> (LuaErrorInformation) obj)
                .map(LuaErrorInformation::getMessage);
        if (errorMessage.isPresent()) {
          getPrinter().println(bind(NL_1, testFile.getName(), errorMessage.get()));
        } else {
          getPrinter().println(bind(NL_2, testFile.getName()));
        }
      }
      testFile.getChildren().forEach(child -> {
        final TestReportNode<?> testSuite = (TestReportNode<?>) child;
        final String name = testSuite.getName();
        final int successes = testSuite.getTheNumberOfSuccesses();
        final int runs = testSuite.getTheNumberOfTests();
        if (testSuite.isSuccess()) {
          getPrinter().println(bind(NL_3, name, successes, runs));
        } else {
          getPrinter().println(bind(NL_4, name, successes, runs));
        }
        testSuite.getChildren().forEach(testCase -> {
          if (testCase.isSuccess()) {
            getPrinter().println(bind(NL_5, testCase.getName()));
          } else {
            getPrinter().println(bind(NL_6, testCase.getName(), testCase.getResultDetail()));
          }
        });
      });
    });
  };

  protected void executeTest(final Builder builder, final String testPath) {
    logger.trace("Builder: {}, Test path: {}", builder, testPath);
    final TestResultCollector testReporter = AthenaContext.getContext().getTestReporter();
    final BuildDetails buildDetails = builder.build(testPath);
    final LuaSource executable = new LuaSource(buildDetails.getResult());
    testReporter.clear();

    logger.trace("Executing test...");
    testReporter.start(testPath);
    final TestResult testResult = new LuaRunner().run(executable);
    final TestReportNode<LuaErrorInformation> testFile = testReporter.getCurrentTestFile();
    if (!testResult.isSuccess()) {
      logger.info("{} failed", testFile.getName());
      testFile.setResult(TestReportNodeResult.Failure);
      testFile.setResultDetail(testResult.getError());
    }

    logger.debug("Test {} => {}", testPath, testResult);
    if (!testResult.isSuccess()) {
      final LuaErrorInformation error = testResult.getError();
      final int lineNumber = error.getLineNumber();
      logger.debug("Lua Script:\n{}",
          executable.toString(lineNumber - 5, lineNumber + 5, singletonList(lineNumber)));
    }
    testReporter.end();
  }

  @Override
  @SuppressWarnings("unchecked")
  public void execute() throws Exception {
    logger.trace("Starting {}...", this);
    final ProjectFile projectFile = readProject();
    final Project project = new Project(".", projectFile);
    final Builder builder = builderFactory.create(project);
    final List<String> testPaths = nvl(projectFile.getTests(), EMPTY_LIST);

    AthenaContext.clear();
    try {
      for (final String testPath : testPaths) {
        executeTest(builder, testPath);
      }
    } finally {
      final AthenaContext context = AthenaContext.getContext();
      final TestResultCollector testResultCollector = context.getTestReporter();
      logger.debug("TestResultCollector: {}", testResultCollector);
      reporter.accept(testResultCollector);
    }
  }
}