001/*
002 * Copyright (C) 2022 - 2023, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.workspaces;
017
018import io.github.ascopes.jct.workspaces.impl.AbstractManagedDirectory;
019import io.github.ascopes.jct.workspaces.impl.RamDirectoryImpl;
020import io.github.ascopes.jct.workspaces.impl.TempDirectoryImpl;
021import java.io.File;
022import java.nio.file.Path;
023import java.util.function.Function;
024import javax.annotation.processing.Filer;
025import org.apiguardian.api.API;
026import org.apiguardian.api.API.Status;
027
028/**
029 * Strategy to use for creating new test directories.
030 *
031 * <p>This is used to define whether to use a totally isolated in-memory file system,
032 * or whether to use temporary directories on the default file system.
033 *
034 * @author Ashley Scopes
035 * @since 0.0.1
036 */
037@API(since = "0.0.1", status = Status.STABLE)
038public enum PathStrategy {
039  /**
040   * Use RAM-based directories for any created directories.
041   *
042   * <p>This is faster as everything remains in-memory. It also prevents the risk of resources
043   * not being tidied up correctly if the JVM is suddenly shut down. Test directories are also kept
044   * isolated from other tests that may be running in parallel, and isolated from the host operating
045   * system.
046   *
047   * <p>If any annotation processors rely on being run in the {@link File default file system}
048   * rather than using {@link Path the NIO path API} or {@link Filer filers} directly, then they
049   * will not be compatible with reading files from this implementation of test directory. In this
050   * situation, users should opt to use {@link #TEMP_DIRECTORIES} instead.
051   *
052   * <p>Some non-Javac compiler implementations (such as ECJ) may also have some difficulties
053   * dealing with these paths.
054   *
055   * The actual implementation used internally will be the first {@link RamFileSystemProvider} that
056   * is present when querying that interface with the Java 
057   * {@link java.util.ServiceLoader Service Loader mechanism}. By default, if no custom implementation
058   * is provided by the user, then an internal implementation is used. This internal implementation is
059   * subject to change at any time (as long as the change is non-breaking), but currently uses
060   * {@link com.google.common.jimfs.Jimfs Google's JIMFS}.
061   */
062  RAM_DIRECTORIES(RamDirectoryImpl::newRamDirectory),
063
064  /**
065   * Use OS-level temporary directories for any created directories.
066   *
067   * <p>This will write files to the OS temporary directory. These files will be deleted once the
068   * owning workspace is closed, but may be missed if the JVM is forcefully terminated or crashes.
069   *
070   * <p>There are fewer guarantees of speed and isolation compared to using
071   * {@link #RAM_DIRECTORIES}. However, you do gain the ability to set a breakpoint and inspect the
072   * contents of the directory in a file explorer.
073   *
074   * <p>Since the temporary directories are usually created on the
075   * {@link File default file system}, they are compatible with any annotation processors or
076   * compiler implementations that expect to be run on the default file system only.
077   *
078   * <p>Some restrictions regarding file naming may be present depending on the platform that
079   * is in use, such as file name lengths on Windows. See your system documentation for more
080   * details.
081   */
082  TEMP_DIRECTORIES(TempDirectoryImpl::newTempDirectory);
083
084  private final Function<String, AbstractManagedDirectory> constructor;
085
086  PathStrategy(Function<String, AbstractManagedDirectory> constructor) {
087    this.constructor = constructor;
088  }
089
090  /**
091   * Create a new instance of the test directory type with the given name.
092   *
093   * <p>Note that calling this directly will return an object that you will have to manually
094   * manage the lifetime for. Failing to do so will result in resources being leaked.
095   *
096   * <p><strong>Users should not call this method by default unless they know what they are
097   * doing.</strong> As a result of this constraint, this method is not part of the public API and
098   * may be subject to change without notice.
099   *
100   * @param name the name to use.
101   * @return the new test directory.
102   */
103  @API(since = "0.0.1", status = Status.INTERNAL)
104  public ManagedDirectory newInstance(String name) {
105    return constructor.apply(name);
106  }
107
108  /**
109   * Determine the default strategy to fall back onto.
110   *
111   * <p>This will be {@link #RAM_DIRECTORIES} by default, but this may be subject to change between
112   * minor versions without notice, so do not rely on this if you are testing code that may be
113   * sensitive to the type of file system being used.
114   *
115   * @return the path strategy to use by default.
116   */
117  public static PathStrategy defaultStrategy() {
118    return RAM_DIRECTORIES;
119  }
120}