001package org.hl7.fhir.validation.testexecutor;
002
003import static org.hl7.fhir.validation.testexecutor.TestModules.JUNIT4_CLASSNAMES;
004import static org.hl7.fhir.validation.testexecutor.TestModules.JUNIT5_MODULE_NAMES;
005import static org.hl7.fhir.validation.testexecutor.TestModules.VALIDATION_MODULE;
006
007import java.io.IOException;
008import java.nio.file.Files;
009import java.nio.file.Path;
010import java.nio.file.Paths;
011import java.util.Arrays;
012import java.util.HashMap;
013import java.util.List;
014import java.util.Map;
015import java.util.stream.Collectors;
016import java.util.stream.Stream;
017
018import javax.annotation.Nonnull;
019
020import org.hl7.fhir.utilities.tests.TestConfig;
021import org.hl7.fhir.utilities.tests.execution.CliTestSummary;
022import org.hl7.fhir.utilities.tests.execution.ModuleTestExecutor;
023import org.hl7.fhir.utilities.tests.execution.junit4.JUnit4TestExecutor;
024import org.hl7.fhir.utilities.tests.execution.junit5.JUnit5ModuleTestExecutor;
025
026import lombok.Getter;
027
028public class TestExecutor {
029
030
031  @Getter private final List<ModuleTestExecutor> jUnit4TestExecutors;
032
033  @Getter private final List<ModuleTestExecutor> jUnit5TestExecutors;
034
035  public TestExecutor(String[] moduleNamesArg) {
036    this(getjUnit4TestExecutors(moduleNamesArg), getjUnit5TestExecutors(moduleNamesArg));
037  }
038  protected TestExecutor(List<ModuleTestExecutor> jUnit4TestExecutors, List<ModuleTestExecutor> jUnit5TestExecutors) {
039    this.jUnit4TestExecutors = jUnit4TestExecutors;
040    this.jUnit5TestExecutors = jUnit5TestExecutors;
041  }
042
043  public static final String TX_CACHE = "txCache";
044
045  private static String SUMMARY_TEMPLATE = "Tests run: %d, Failures: %d, Errors: %d, Skipped: %d";
046  private static final String DOT_PLACEHOLDER = new String(new char[53]).replace("\0", ".");
047
048  private static final int MAX_NAME_LENGTH = 50;
049
050  private String getModuleResultLine(String moduleName, CliTestSummary moduleTestSummary) {
051    return getModuleNameAndSpacer(moduleName)
052      + " "
053      + getModuleResultString(moduleTestSummary);
054  }
055
056  private String getModuleNameAndSpacer(String moduleName) {
057    return moduleName.length() < MAX_NAME_LENGTH
058      ? moduleName + " " + DOT_PLACEHOLDER.substring(moduleName.length() + 1)
059      : moduleName.substring(0, MAX_NAME_LENGTH) + "...";
060  }
061
062  private String getModuleResultString(CliTestSummary cliTestSummary) {
063    if (cliTestSummary.getTestsFoundCount() == 0) {
064      return "NO TESTS";
065    }
066
067    return cliTestSummary.getTestsFailedCount() == 0 && cliTestSummary.getTestsAbortedCount() == 0
068      ? "PASSED"
069      : "FAILED";
070  }
071
072  public static boolean pathExistsAsDirectory(String directoryPath) {
073    Path path = Paths.get(directoryPath);
074    return Files.exists(path)
075      && Files.isDirectory(path);
076  }
077
078  public void executeTests(String classNameFilterArg, String txCacheDirectoryPath, String testCasesDirectoryPath) {
079
080    long start = System.currentTimeMillis();
081    //Our lone JUnit 4 test.
082    List<ModuleTestExecutor> jUnit4TestExecutors = getJUnit4TestExecutors();
083
084    TestConfig.getInstance().setRebuildCache(true);
085
086    setUpDirectories(txCacheDirectoryPath, testCasesDirectoryPath);
087
088    List<ModuleTestExecutor> jUnit5TestExecutors = getJUnit5TestExecutors();
089
090    long testsFoundCount = 0;
091    long testsFailedCount = 0;
092    long testsAbortedCount = 0;
093    long testsSkippedCount = 0;
094
095    final Map<String, CliTestSummary> moduleResultMap = new HashMap<>();
096
097    final List<ModuleTestExecutor> orderedModuleTestExecutors = Stream.concat(jUnit5TestExecutors.stream(), jUnit4TestExecutors.stream()).collect(Collectors.toList());
098
099    for (ModuleTestExecutor moduleTestExecutor : orderedModuleTestExecutors) {
100      final CliTestSummary testExecutionSummary = moduleTestExecutor.executeTests(System.out, classNameFilterArg);
101      testsFoundCount += testExecutionSummary.getTestsFoundCount();
102      testsFailedCount += testExecutionSummary.getTestsFailedCount();
103      testsAbortedCount += testExecutionSummary.getTestsAbortedCount();
104      testsSkippedCount += testExecutionSummary.getTestsSkippedCount();
105      moduleResultMap.put(moduleTestExecutor.getModuleName(), testExecutionSummary);
106    }
107
108    System.out.println("\n\nAll Tests completed.");
109
110    for (ModuleTestExecutor moduleTestExecutor : orderedModuleTestExecutors) {
111      ModuleTestExecutor.printSummmary(System.out, moduleResultMap.get(moduleTestExecutor.getModuleName()), moduleTestExecutor.getModuleName());
112    }
113
114    System.out.println("\nModule Results:\n");
115
116    for (ModuleTestExecutor moduleTestExecutor : orderedModuleTestExecutors) {
117      System.out.println(getModuleResultLine(moduleTestExecutor.getModuleName(), moduleResultMap.get(moduleTestExecutor.getModuleName())));
118    }
119
120    System.out.println();
121    System.out.println(String.format(SUMMARY_TEMPLATE, testsFoundCount, testsFailedCount, testsAbortedCount, testsSkippedCount));
122    System.out.println("\nCompleted in " + (System.currentTimeMillis() - start) + "ms");
123
124  }
125
126  protected void setUpDirectories(String txCacheDirectoryPath, String testCasesDirectoryPath) {
127    if (testCasesDirectoryPath != null
128      && pathExistsAsDirectory(testCasesDirectoryPath)
129    ) {
130      TestConfig.getInstance().setFhirTestCasesDirectory(testCasesDirectoryPath);
131    } else {
132      throw new RuntimeException("fhir-test-cases directory does not exist: " + testCasesDirectoryPath);
133    }
134    try {
135      String txCacheDirectory = getOrCreateTxCacheDirectory(txCacheDirectoryPath);
136      if (!pathExistsAsDirectory(testCasesDirectoryPath)) {
137        throw new RuntimeException("txCache directory does not exist: " + txCacheDirectory);
138      }
139      TestConfig.getInstance().setTxCacheDirectory(txCacheDirectory);
140    } catch (IOException e) {
141      throw new RuntimeException("Unable to create temporary resource directory.", e);
142    }
143  }
144
145  private static String getOrCreateTxCacheDirectory(String txCacheDirectoryParam) throws IOException {
146    final String txCacheDirectory;
147
148    if (txCacheDirectoryParam != null) {
149      txCacheDirectory = txCacheDirectoryParam;
150    } else {
151      txCacheDirectory = Files.createTempDirectory("validator-test-tx-cache").toFile().getAbsolutePath();
152      TxCacheResourceExtractor.extractTxCacheResources(txCacheDirectory);
153    }
154
155    return txCacheDirectory;
156  }
157
158  @Nonnull
159  public static List<ModuleTestExecutor> getjUnit5TestExecutors(String[] moduleNamesArg) {
160    final String[] moduleNames = moduleNamesArg == null ? JUNIT5_MODULE_NAMES : moduleNamesArg;
161    return Arrays.stream(moduleNames)
162      .map(moduleName -> JUnit5ModuleTestExecutor.getStandardModuleTestExecutor(moduleName))
163      .collect(Collectors.toList());
164  }
165
166  @Nonnull
167  public static List<ModuleTestExecutor> getjUnit4TestExecutors(String[] moduleNamesArg) {
168
169    final List<ModuleTestExecutor> jUnit4TestExecutors = Arrays.asList(new JUnit4TestExecutor(VALIDATION_MODULE, JUNIT4_CLASSNAMES));
170
171    if (moduleNamesArg == null) {
172      return jUnit4TestExecutors;
173    }
174
175    return  Arrays.stream(moduleNamesArg).anyMatch(moduleName -> VALIDATION_MODULE.equals(moduleName))
176      ? jUnit4TestExecutors
177      : Arrays.asList();
178  }
179}