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

io.quarkus.test.QuarkusDevModeTest Maven / Gradle / Ivy

Go to download

A runner for unit tests, intended for testing Quarkus rather than for end user consumption.

There is a newer version: 3.17.5
Show newest version
package io.quarkus.test;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.stream.Stream;

import org.jboss.logmanager.Logger;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.exporter.ExplodedExporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstanceFactory;
import org.junit.jupiter.api.extension.TestInstanceFactoryContext;
import org.junit.jupiter.api.extension.TestInstantiationException;

import io.quarkus.deployment.dev.CompilationProvider;
import io.quarkus.deployment.dev.DevModeContext;
import io.quarkus.deployment.dev.DevModeMain;
import io.quarkus.deployment.util.FileUtil;
import io.quarkus.dev.appstate.ApplicationStateNotification;
import io.quarkus.dev.testing.TestScanningLock;
import io.quarkus.maven.dependency.GACT;
import io.quarkus.paths.PathList;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.test.common.GroovyClassValue;
import io.quarkus.test.common.PathTestHelper;
import io.quarkus.test.common.PropertyTestUtil;
import io.quarkus.test.common.TestConfigUtil;
import io.quarkus.test.common.TestResourceManager;
import io.quarkus.test.common.http.TestHTTPResourceManager;

/**
 * A test extension for black-box testing of Quarkus development mode in extensions.
 * 

* Intended for use by extension developers testing their extension functionality in dev mode. *

* Note that unlike {@link QuarkusUnitTest}: *

    *
  • Tests run in black-box mode: the classloader of test methods * does not give access to the running Quarkus application, * so things like CDI will not work. * You should define REST endpoints that perform the actions you want to simulate in your tests, * and call those REST endpoints in your tests using e.g. RestAssured.
  • *
  • Each test method will run in a clean deployment. * This is necessary to prevent undefined behaviour by making sure the * deployment starts in a clean state for each test.
  • *
  • Tests do not run with {@link io.quarkus.runtime.LaunchMode#TEST} but rather with * {@link io.quarkus.runtime.LaunchMode#DEVELOPMENT}. * This is necessary to ensure dev mode is tested correctly. * A side effect of this is that the tests will run on port 8080 by default instead of port 8081. *
*/ public class QuarkusDevModeTest implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, TestInstanceFactory { private static final Logger rootLogger; public static final OpenOption[] OPEN_OPTIONS = { StandardOpenOption.SYNC, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE }; private Handler[] originalRootLoggerHandlers; static { System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager"); java.util.logging.Logger logger = LogManager.getLogManager().getLogger(""); if (!(logger instanceof org.jboss.logmanager.Logger)) { throw new IllegalStateException( "QuarkusDevModeTest must be used with the the JBoss LogManager. See https://quarkus.io/guides/logging#how-to-configure-logging-for-quarkustest for an example of how to configure it in Maven."); } rootLogger = (org.jboss.logmanager.Logger) logger; } private DevModeMain devModeMain; private Path deploymentDir; private Supplier archiveProducer; private Supplier testArchiveProducer; private List codeGenSources = Collections.emptyList(); private String logFileName; private InMemoryLogHandler inMemoryLogHandler = new InMemoryLogHandler((r) -> false); private Path deploymentSourceParentPath; private Path deploymentSourcePath; private Path deploymentResourcePath; private Path deploymentTestSourceParentPath; private Path deploymentTestSourcePath; private Path deploymentTestResourcePath; private Path projectSourceRoot; private Path testLocation; private String[] commandLineArgs = new String[0]; private final Map oldSystemProps = new HashMap<>(); private final Map buildSystemProperties = new HashMap<>(); private boolean allowFailedStart = false; private static final List compilationProviders; static { List providers = new ArrayList<>(); for (CompilationProvider provider : ServiceLoader.load(CompilationProvider.class)) { providers.add(provider); } compilationProviders = Collections.unmodifiableList(providers); } public Supplier getArchiveProducer() { return archiveProducer; } public QuarkusDevModeTest setArchiveProducer(Supplier archiveProducer) { this.archiveProducer = archiveProducer; return this; } /** * Customize the application root. * * @param applicationRootConsumer * @return self */ public QuarkusDevModeTest withApplicationRoot(Consumer applicationRootConsumer) { Objects.requireNonNull(applicationRootConsumer); return setArchiveProducer(() -> { JavaArchive jar = ShrinkWrap.create(JavaArchive.class); applicationRootConsumer.accept(jar); return jar; }); } /** * Use an empty application for the test * * @return self */ public QuarkusDevModeTest withEmptyApplication() { return withApplicationRoot(new Consumer() { @Override public void accept(JavaArchive javaArchive) { } }); } public QuarkusDevModeTest setTestArchiveProducer(Supplier testArchiveProducer) { this.testArchiveProducer = testArchiveProducer; return this; } /** * Customize the application root. * * @param applicationRootConsumer * @return self */ public QuarkusDevModeTest withTestArchive(Consumer testArchiveConsumer) { Objects.requireNonNull(testArchiveConsumer); return setTestArchiveProducer(() -> { JavaArchive jar = ShrinkWrap.create(JavaArchive.class); testArchiveConsumer.accept(jar); return jar; }); } public QuarkusDevModeTest setCodeGenSources(String... codeGenSources) { this.codeGenSources = Arrays.asList(codeGenSources); return this; } public QuarkusDevModeTest setLogFileName(String logFileName) { this.logFileName = logFileName; return this; } public QuarkusDevModeTest setLogRecordPredicate(Predicate predicate) { this.inMemoryLogHandler = new InMemoryLogHandler(predicate); return this; } public List getLogRecords() { return inMemoryLogHandler.records; } public void clearLogRecords() { inMemoryLogHandler.clearRecords(); } public QuarkusDevModeTest setBuildSystemProperty(String name, String value) { buildSystemProperties.put(name, value); return this; } public Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) throws TestInstantiationException { try { Object actualTestInstance = factoryContext.getTestClass().getDeclaredConstructor().newInstance(); TestHTTPResourceManager.inject(actualTestInstance); return actualTestInstance; } catch (Exception e) { throw new TestInstantiationException("Unable to create test proxy", e); } } @Override public void beforeAll(ExtensionContext context) throws Exception { TestConfigUtil.cleanUp(); GroovyClassValue.disable(); //set the right launch mode in the outer CL, used by the HTTP host config source LaunchMode.set(LaunchMode.DEVELOPMENT); originalRootLoggerHandlers = rootLogger.getHandlers(); rootLogger.addHandler(inMemoryLogHandler); } @Override public void beforeEach(ExtensionContext extensionContext) throws Exception { if (archiveProducer == null) { throw new RuntimeException("QuarkusDevModeTest does not have archive producer set"); } ExclusivityChecker.checkTestType(extensionContext, QuarkusDevModeTest.class); if (logFileName != null) { PropertyTestUtil.setLogFileProperty(logFileName); } else { PropertyTestUtil.setLogFileProperty(); } ExtensionContext.Store store = extensionContext.getRoot().getStore(ExtensionContext.Namespace.GLOBAL); if (store.get(TestResourceManager.class.getName()) == null) { TestResourceManager testResourceManager = new TestResourceManager(extensionContext.getRequiredTestClass()); testResourceManager.init(null); Map properties = testResourceManager.start(); TestResourceManager tm = testResourceManager; store.put(TestResourceManager.class.getName(), testResourceManager); store.put(TestResourceManager.CLOSEABLE_NAME, new ExtensionContext.Store.CloseableResource() { @Override public void close() throws Throwable { tm.close(); } }); } TestResourceManager tm = (TestResourceManager) store.get(TestResourceManager.class.getName()); //dev mode tests just use system properties //we set them here and clear them in afterAll //so they don't interfere with other tests for (Map.Entry i : tm.getConfigProperties().entrySet()) { oldSystemProps.put(i.getKey(), System.getProperty(i.getKey())); if (i.getValue() == null) { System.clearProperty(i.getKey()); } else { System.setProperty(i.getKey(), i.getValue()); } } Class testClass = extensionContext.getRequiredTestClass(); try { deploymentDir = Files.createTempDirectory("quarkus-dev-mode-test"); testLocation = PathTestHelper.getTestClassesLocation(testClass); //TODO: this is a huge hack, at the moment this just guesses the source location //this can be improved, but as this is for testing extensions it is probably fine for now String sourcePath = System.getProperty("quarkus.test.source-path"); if (sourcePath == null) { //TODO: massive hack, make sure this works in eclipse projectSourceRoot = testLocation.getParent().getParent().resolve("src/test/java"); } else { projectSourceRoot = Paths.get(sourcePath); } // TODO: again a hack, assumes the sources dir is one dir above java sources path Path projectSourceParent = projectSourceRoot.getParent(); DevModeContext context = exportArchive(deploymentDir, projectSourceRoot, projectSourceParent); context.setBaseName(extensionContext.getDisplayName() + " (QuarkusDevModeTest)"); context.setArgs(commandLineArgs); context.setTest(true); context.setAbortOnFailedStart(!allowFailedStart); context.getBuildSystemProperties().put("quarkus.banner.enabled", "false"); context.getBuildSystemProperties().put("quarkus.console.disable-input", "true"); //surefire communicates via stdin, we don't want the test to be reading input context.getBuildSystemProperties().putAll(buildSystemProperties); devModeMain = new DevModeMain(context); devModeMain.start(); ApplicationStateNotification.waitForApplicationStart(); } catch (Exception e) { if (allowFailedStart) { e.printStackTrace(); } else { throw (e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e)); } } } @Override public void afterAll(ExtensionContext context) throws Exception { for (Map.Entry e : oldSystemProps.entrySet()) { if (e.getValue() == null) { System.clearProperty(e.getKey()); } else { System.setProperty(e.getKey(), e.getValue()); } } rootLogger.setHandlers(originalRootLoggerHandlers); inMemoryLogHandler.clearRecords(); inMemoryLogHandler.setFilter(null); ClearCache.clearCaches(); TestConfigUtil.cleanUp(); } @Override public void afterEach(ExtensionContext extensionContext) throws Exception { try { if (devModeMain != null) { devModeMain.close(); devModeMain = null; } } finally { if (deploymentDir != null) { FileUtil.deleteDirectory(deploymentDir); } } inMemoryLogHandler.clearRecords(); } private DevModeContext exportArchive(Path deploymentDir, Path testSourceDir, Path testSourcesParentDir) { try { deploymentSourcePath = deploymentDir.resolve("src/main/java"); deploymentSourceParentPath = deploymentDir.resolve("src/main"); deploymentResourcePath = deploymentDir.resolve("src/main/resources"); Path classes = deploymentDir.resolve("target/classes"); Path targetDir = deploymentDir.resolve("target"); Path cache = deploymentDir.resolve("target/dev-cache"); Files.createDirectories(deploymentSourcePath); Files.createDirectories(deploymentResourcePath); Files.createDirectories(classes); Files.createDirectories(cache); //first we export the archive //then we attempt to generate a source tree JavaArchive archive = archiveProducer.get(); archive.as(ExplodedExporter.class).exportExplodedInto(classes.toFile()); copyFromSource(testSourceDir, deploymentSourcePath, classes); copyCodeGenSources(testSourcesParentDir, deploymentSourceParentPath, codeGenSources); //now copy resources //we assume everything that is not a .class file is a resource //resources are handled differently to sources as they are often not in the same location //in the FS, or are dynamically created try (Stream stream = Files.walk(classes)) { stream.forEach(s -> { if (s.toString().endsWith(".class") || Files.isDirectory(s)) { return; } String relative = classes.relativize(s).toString(); try { try (InputStream in = Files.newInputStream(s)) { byte[] data = FileUtil.readFileContents(in); Path resolved = deploymentResourcePath.resolve(relative); Files.createDirectories(resolved.getParent()); Files.write(resolved, data, OPEN_OPTIONS); } } catch (IOException e) { throw new UncheckedIOException(e); } }); } //debugging code ExportUtil.exportToQuarkusDeploymentPath(archive); DevModeContext context = new DevModeContext(); context.setCacheDir(cache.toFile()); DevModeContext.ModuleInfo.Builder moduleBuilder = new DevModeContext.ModuleInfo.Builder() .setArtifactKey(GACT.fromString("io.quarkus.test:app-under-test")) .setProjectDirectory(deploymentDir.toAbsolutePath().toString()) .setSourcePaths(PathList.of(deploymentSourcePath.toAbsolutePath())) .setClassesPath(classes.toAbsolutePath().toString()) .setResourcePaths(PathList.of(deploymentResourcePath.toAbsolutePath())) .setResourcesOutputPath(classes.toAbsolutePath().toString()) .setSourceParents(PathList.of(deploymentSourceParentPath.toAbsolutePath())) .setPreBuildOutputDir(targetDir.resolve("generated-sources").toAbsolutePath().toString()) .setTargetDir(targetDir.toAbsolutePath().toString()); //now tests, if required if (testArchiveProducer != null) { deploymentTestSourcePath = deploymentDir.resolve("src/test/java"); deploymentTestSourceParentPath = deploymentDir.resolve("src/test"); deploymentTestResourcePath = deploymentDir.resolve("src/test/resources"); Path testClasses = deploymentDir.resolve("target/test-classes"); Files.createDirectories(deploymentTestSourcePath); Files.createDirectories(deploymentTestResourcePath); Files.createDirectories(testClasses); //first we export the archive //then we attempt to generate a source tree JavaArchive testArchive = testArchiveProducer.get(); testArchive.as(ExplodedExporter.class).exportExplodedInto(testClasses.toFile()); copyFromSource(testSourceDir, deploymentTestSourcePath, testClasses); //now copy resources //we assume everything that is not a .class file is a resource //resources are handled differently to sources as they are often not in the same location //in the FS, or are dynamically created try (Stream stream = Files.walk(testClasses)) { stream.forEach(s -> { if (s.toString().endsWith(".class") || Files.isDirectory(s)) { return; } String relative = testClasses.relativize(s).toString(); try { try (InputStream in = Files.newInputStream(s)) { byte[] data = FileUtil.readFileContents(in); Path resolved = deploymentTestResourcePath.resolve(relative); Files.createDirectories(resolved.getParent()); Files.write(resolved, data, OPEN_OPTIONS); } } catch (IOException e) { throw new UncheckedIOException(e); } }); } moduleBuilder .setTestSourcePaths(PathList.of(deploymentTestSourcePath.toAbsolutePath())) .setTestClassesPath(testClasses.toAbsolutePath().toString()) .setTestResourcePaths(PathList.of(deploymentTestResourcePath.toAbsolutePath())) .setTestResourcesOutputPath(testClasses.toAbsolutePath().toString()); } context.setApplicationRoot( moduleBuilder .build()); return context; } catch (Exception e) { throw new RuntimeException("Unable to create the archive", e); } } private void copyCodeGenSources(Path testSourcesParent, Path deploymentSourceParentPath, List codeGenSources) { for (String codeGenDirName : codeGenSources) { Path codeGenSource = testSourcesParent.resolve(codeGenDirName); try { Path target = deploymentSourceParentPath.resolve(codeGenDirName); try (Stream files = Files.walk(codeGenSource)) { files.forEach( file -> { Path targetPath = target.resolve(codeGenSource.relativize(file)); try { Files.copy(file, targetPath); } catch (IOException e) { throw new RuntimeException( "Failed to copy file : " + file + " to " + targetPath.toAbsolutePath().toString()); } }); } } catch (IOException e) { throw new RuntimeException("Failed to copy code gen directory", e); } } } /** * Modifies a source file. * * @param sourceFile The unqualified name of the source file to modify * @param mutator A function that will modify the source code */ public void modifySourceFile(String sourceFile, Function mutator) { modifyFile(sourceFile, mutator, deploymentSourcePath); } /** * Modifies a file * * @param file file path relative to the project's sources parent dir (`src/main` for Maven) * @param mutator A function that will modify the file */ public void modifyFile(String file, Function mutator) { modifyPath(mutator, deploymentSourceParentPath, deploymentSourceParentPath.resolve(file)); } /** * Modifies a source file. * * @param sourceFile The Class corresponding to the source file to modify * @param mutator A function that will modify the source code */ public void modifySourceFile(Class sourceFile, Function mutator) { modifyFile(sourceFile.getSimpleName() + ".java", mutator, deploymentSourcePath); } /** * Modifies a source file. * * @param sourceFile The Class corresponding to the source file to modify * @param mutator A function that will modify the source code */ public void modifyTestSourceFile(Class sourceFile, Function mutator) { modifyFile(sourceFile.getSimpleName() + ".java", mutator, deploymentTestSourcePath); } /** * Adds the source file that corresponds to the given class to the deployment * * @param sourceFile */ public void addSourceFile(Class sourceFile) { Path path = findTargetSourceFilesForPath(projectSourceRoot, deploymentSourcePath, testLocation, testLocation.resolve(sourceFile.getName().replace(".", "/") + ".class")); long old = modTime(path.getParent()); copySourceFilesForClass(projectSourceRoot, deploymentSourcePath, testLocation, testLocation.resolve(sourceFile.getName().replace(".", "/") + ".class")); // since this is a new file addition, even wait for the parent dir's last modified timestamp to change sleepForFileChanges(path.getParent(), old); } public String[] getCommandLineArgs() { return commandLineArgs; } public QuarkusDevModeTest setCommandLineArgs(String[] commandLineArgs) { this.commandLineArgs = commandLineArgs; return this; } void modifyFile(String name, Function mutator, Path path) { TestScanningLock.lockForTests(); try { AtomicBoolean found = new AtomicBoolean(false); try (Stream sources = Files.walk(path)) { sources.forEach(s -> { if (s.endsWith(name)) { found.set(true); modifyPath(mutator, path, s); } }); } catch (IOException e) { throw new UncheckedIOException(e); } if (!found.get()) { throw new IllegalArgumentException("File " + name + " was not part of the test application"); } } finally { TestScanningLock.unlockForTests(); } } private void modifyPath(Function mutator, Path sourceDirectory, Path input) { TestScanningLock.lockForTests(); try { long old = modTime(input); long oldSrc = modTime(sourceDirectory); byte[] data; try (InputStream in = Files.newInputStream(input)) { data = FileUtil.readFileContents(in); } String oldContent = new String(data, StandardCharsets.UTF_8); String content = mutator.apply(oldContent); if (content.equals(oldContent)) { throw new RuntimeException("File was not modified, mutator function had no effect"); } Files.write(input, content.getBytes(StandardCharsets.UTF_8), OPEN_OPTIONS); sleepForFileChanges(sourceDirectory, oldSrc); sleepForFileChanges(input, old); } catch (IOException e) { throw new UncheckedIOException(e); } finally { TestScanningLock.unlockForTests(); } } private void sleepForFileChanges(Path path, long oldTime) { try { //we avoid modifying the file twice //this can cause intermittent failures in the continuous testing tests long fm = modTime(path); if (fm > oldTime) { return; } //we want to make sure the last modified time is larger than both the current time //and the current last modified time. Some file systems only resolve file //time to the nearest second, so this is necessary for dev mode to pick up the changes long timeToBeat = Math.max(System.currentTimeMillis(), modTime(path)); for (;;) { Thread.sleep(1000); Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis())); fm = modTime(path); Thread.sleep(100); if (fm > timeToBeat) { return; } } } catch (Exception e) { throw new RuntimeException(e); } } private long modTime(Path path) { try { return Files.getLastModifiedTime(path).toMillis(); } catch (IOException e) { throw new RuntimeException(e); } } /** * Adds or overwrites a resource file with the given data. The path is an absolute path into to * the deployment resources directory */ public void modifyResourceFile(String path, Function mutator) { Path resourcePath = deploymentResourcePath.resolve(path); internalModifyResource(mutator, resourcePath); } private void internalModifyResource(Function mutator, Path resourcePath) { try { long old = modTime(resourcePath); byte[] data; try (InputStream in = Files.newInputStream(resourcePath)) { data = FileUtil.readFileContents(in); } String content = new String(data, StandardCharsets.UTF_8); content = mutator.apply(content); Files.write(resourcePath, content.getBytes(StandardCharsets.UTF_8), OPEN_OPTIONS); sleepForFileChanges(resourcePath, old); } catch (IOException e) { throw new UncheckedIOException(e); } } /** * Adds or overwrites a resource file with the given data. The path is an absolute path into to * the deployment resources directory */ public void modifyTestResourceFile(String path, Function mutator) { Path resourcePath = deploymentTestResourcePath.resolve(path); internalModifyResource(mutator, resourcePath); } /** * Adds or overwrites a resource file with the given data. The path is an absolute path into to * the deployment resources directory */ public void addResourceFile(String path, byte[] data) { final Path resourceFilePath = deploymentResourcePath.resolve(path); long oldParent = modTime(resourceFilePath.getParent()); try { Files.write(resourceFilePath, data, OPEN_OPTIONS); } catch (IOException e) { throw new UncheckedIOException(e); } // since this is a new file addition, even wait for the parent dir's last modified timestamp to change sleepForFileChanges(resourceFilePath.getParent(), oldParent); } /** * Deletes a resource file. The path is an absolute path into to * the deployment resources directory */ public void deleteResourceFile(String path) { final Path resourceFilePath = deploymentResourcePath.resolve(path); long old = modTime(resourceFilePath.getParent()); long timeout = System.currentTimeMillis() + 5000; //in general there is a potential race here //if you serve a file you will send the data to the client, then close the resource //this means that by the time the client request is run the file may not //have been closed yet, as the test sees the response as being complete after the last data is send //we wait up to 5s for this condition to be resolved for (;;) { try { Files.delete(resourceFilePath); break; } catch (IOException e) { try { Thread.sleep(50); } catch (InterruptedException ex) { //ignore } if (System.currentTimeMillis() < timeout) { continue; } throw new UncheckedIOException(e); } } // wait for last modified time of the parent to get updated sleepForFileChanges(resourceFilePath.getParent(), old); } /** * Adds or overwrites a resource file with the given data encoded into UTF-8. The path is an absolute path into to * the deployment resources directory */ public void addResourceFile(String path, String data) { addResourceFile(path, data.getBytes(StandardCharsets.UTF_8)); } private void copyFromSource(Path projectSourcesDir, Path deploymentSourcesDir, Path classesDir) throws IOException { try (Stream classes = Files.walk(classesDir)) { classes.forEach((c) -> { if (Files.isDirectory(c) || !c.toString().endsWith(".class")) { return; } copySourceFilesForClass(projectSourcesDir, deploymentSourcesDir, classesDir, c); }); } } private Path copySourceFilesForClass(Path projectSourcesDir, Path deploymentSourcesDir, Path classesDir, Path classFile) { for (CompilationProvider provider : compilationProviders) { Path source = provider.getSourcePath(classFile, PathList.of(projectSourcesDir.toAbsolutePath()), classesDir.toAbsolutePath().toString()); if (source != null) { String relative = projectSourcesDir.relativize(source).toString(); try (InputStream in = Files.newInputStream(source)) { byte[] data = FileUtil.readFileContents(in); Path resolved = deploymentSourcesDir.resolve(relative); Files.createDirectories(resolved.getParent()); Files.write(resolved, data, OPEN_OPTIONS); return resolved; } catch (IOException e) { throw new UncheckedIOException(e); } } } return null; } private Path findTargetSourceFilesForPath(Path projectSourcesDir, Path deploymentSourcesDir, Path classesDir, Path classFile) { for (CompilationProvider provider : compilationProviders) { Path source = provider.getSourcePath(classFile, PathList.of(projectSourcesDir.toAbsolutePath()), classesDir.toAbsolutePath().toString()); if (source != null) { String relative = projectSourcesDir.relativize(source).toString(); try { Path resolved = deploymentSourcesDir.resolve(relative); Files.createDirectories(resolved.getParent()); return resolved; } catch (IOException e) { throw new UncheckedIOException(e); } } } throw new RuntimeException("Could not find source file for " + classFile); } public boolean isAllowFailedStart() { return allowFailedStart; } public QuarkusDevModeTest setAllowFailedStart(boolean allowFailedStart) { this.allowFailedStart = allowFailedStart; return this; } static class ChangeSet { final String name; final Function mutator; final Path path; ChangeSet(String name, Function mutator, Path path) { this.name = name; this.mutator = mutator; this.path = path; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy