package org.netbeans.gradle.project.api.entry; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import org.jtrim.utils.ExceptionHelper; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.netbeans.api.project.Project; import org.netbeans.gradle.project.NbGradleProject; import org.netbeans.gradle.project.properties.ScriptPlatform; import org.netbeans.gradle.project.properties.global.CommonGlobalSettings; import org.netbeans.gradle.project.util.CustomGlobalSettingsRule; import org.netbeans.gradle.project.util.MockServicesRule; import org.netbeans.gradle.project.util.NbConsumer; import org.openide.filesystems.FileObject; public final class SampleProjectRule implements TestRule { private final String resourceName; private final TestRule[] wrapperRules; private final AtomicReference<SampleGradleProject> projectRef; private final ReentrantLock loadedProjectsLock; private Map<FileObject, Project> loadedProjects; public SampleProjectRule(String resourceName, TestRule... wrapperRules) { ExceptionHelper.checkNotNullArgument(resourceName, "resourceName"); this.resourceName = resourceName; this.wrapperRules = wrapperRules.clone(); this.projectRef = new AtomicReference<>(null); this.loadedProjectsLock = new ReentrantLock(); this.loadedProjects = null; ExceptionHelper.checkNotNullElements(this.wrapperRules, "wrapperRules"); } private static Map<FileObject, Project> newProjectContainer() { return new HashMap<>(); } public static SampleProjectRule getStandardRule( String resourceName, final NbConsumer<CommonGlobalSettings> additionalConfig, Class<?>... services) { NbConsumer<CommonGlobalSettings> settingsConfigurer = new NbConsumer<CommonGlobalSettings>() { @Override public void accept(CommonGlobalSettings settings) { settings.gradleLocation().setValue(SampleGradleProject.DEFAULT_GRADLE_TARGET); settings.gradleJvmArgs().setValue(Arrays.asList("-Xmx256m")); settings.defaultJdk().setValue(ScriptPlatform.getDefault()); additionalConfig.accept(settings); } }; return new SampleProjectRule(resourceName, new CustomGlobalSettingsRule(settingsConfigurer), new MockServicesRule(services)); } public static SampleProjectRule getStandardRule(String resourceName, Class<?>... services) { NbConsumer<CommonGlobalSettings> additionalConfig = new NbConsumer<CommonGlobalSettings>() { @Override public void accept(CommonGlobalSettings settings) { } }; return getStandardRule(resourceName, additionalConfig, services); } private void introduceProject(Project project) { loadedProjectsLock.lock(); try { if (loadedProjects != null) { loadedProjects.put(project.getProjectDirectory(), project); } } finally { loadedProjectsLock.unlock(); } } public SampleGradleProject getSampleProject() { SampleGradleProject project = projectRef.get(); if (project == null) { throw new IllegalStateException("Rule is inactive."); } return project; } private static void waitAllLoaded(Collection<? extends Project> projects) throws TimeoutException { for (Project project: projects) { NbGradleProject gradleProject = project.getLookup().lookup(NbGradleProject.class); if (gradleProject != null) { waitLoaded(gradleProject); } } } private static void waitLoaded(NbGradleProject project) throws TimeoutException { if (!project.tryWaitForLoadedProject(3, TimeUnit.MINUTES)) { throw new TimeoutException("Project was not loaded until the timeout elapsed: " + Arrays.asList(project.getProjectDirectoryAsPath())); } } public Project getUnloadedProject(String... projectPath) throws IOException { Project result = getSampleProject().getUnloadedProject(projectPath); introduceProject(result); return result; } public NbGradleProject loadProject(String... projectPath) throws IOException { Thread.interrupted(); NbGradleProject result = getSampleProject().loadProject(projectPath); introduceProject(result); return result; } public NbGradleProject loadAndWaitProject(String... projectPath) throws IOException, TimeoutException { NbGradleProject result = loadProject(projectPath); waitLoaded(result); return result; } public NbGradleProject loadSingleProject() throws IOException { Thread.interrupted(); NbGradleProject result = getSampleProject().loadSingleProject(); introduceProject(result); return result; } public NbGradleProject loadAndWaitSingleProject() throws IOException, TimeoutException { NbGradleProject result = loadSingleProject(); waitLoaded(result); return result; } @Override public Statement apply(final Statement base, final Description description) { Statement result = new Statement() { @Override public void evaluate() throws Throwable { evaluate0(base, description); } }; for (int i = wrapperRules.length - 1; i >= 0; i--) { result = wrapperRules[i].apply(result, description); } return result; } private void evaluate0(Statement base, Description description) throws Throwable { SampleGradleProject project = SampleGradleProject.createProject(description.getTestClass(), resourceName); try { loadedProjectsLock.lock(); try { loadedProjects = newProjectContainer(); } finally { loadedProjectsLock.unlock(); } if (!projectRef.compareAndSet(null, project)) { project.close(); throw new IllegalStateException("Rule was called concurrently."); } base.evaluate(); } finally { project = projectRef.getAndSet(null); if (project != null) { try { waitAllLoaded(getAndClearAllLoaded()); } finally { project.close(); } } } } private Collection<Project> getAndClearAllLoaded() { loadedProjectsLock.lock(); try { Map<?, Project> projects = loadedProjects; if (projects == null) { return Collections.emptySet(); } loadedProjects = null; return new ArrayList<>(projects.values()); } finally { loadedProjectsLock.unlock(); } } }