Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/improve test speed #456

Merged
merged 9 commits into from
Sep 23, 2019
9 changes: 4 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ before_install:
install: true
script:
- ./.ci/ci.sh
# lines commented out below are a temporary workaround for https://github.com/diffplug/spotless/issues/429
before_cache:
# - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
# - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
- rm -fr plugin-maven/build/localMavenRepository/com/diffplug/spotless/
cache:
directories:
# - $HOME/.gradle/caches/
# - $HOME/.gradle/wrapper/
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.m2/
- plugin-maven/build/localMavenRepository/
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ You might be looking for:
* Fixes [#410](https://github.com/diffplug/spotless/issues/410) AccessDeniedException in MinGW/ GitBash.
* Also fixes occasional [hang on NFS due to filesystem timers](https://github.com/diffplug/spotless/pull/407#issuecomment-514824364).
* Eclipse-based formatters used to leave temporary files around ([#447](https://github.com/diffplug/spotless/issues/447)). This is now fixed, but only for eclipse 4.12+, no back-port to older Eclipse formatter versions is planned. ([#451](https://github.com/diffplug/spotless/issues/451))
* Minor change to `TestProvisioner`, which should fix the cache-breaking issues, allowing us to speed-up the CI builds a bit.

### Version 1.24.1 - August 12th 2018 (javadoc [lib](https://diffplug.github.io/spotless/javadoc/spotless-lib/1.24.1/) [lib-extra](https://diffplug.github.io/spotless/javadoc/spotless-lib-extra/1.24.1/), artifact [lib]([jcenter](https://bintray.com/diffplug/opensource/spotless-lib), [lib-extra]([jcenter](https://bintray.com/diffplug/opensource/spotless-lib-extra)))

Expand Down
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name=spotless
description=Spotless - keep your code spotless with Gradle
org=diffplug

org.gradle.parallel=false

group=com.diffplug.spotless

versionLib=1.25.0-SNAPSHOT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@
import org.assertj.core.api.Assertions;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.testfixtures.ProjectBuilder;
import org.junit.Test;

import com.diffplug.common.base.StringPrinter;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.ResourceHarness;
import com.diffplug.spotless.TestProvisioner;
import com.diffplug.spotless.extra.integration.DiffMessageFormatter;

public class DiffMessageFormatterTest extends ResourceHarness {
Expand All @@ -42,7 +42,7 @@ private SpotlessTask create(File... files) throws IOException {
}

private SpotlessTask create(List<File> files) throws IOException {
Project project = ProjectBuilder.builder().withProjectDir(rootFolder()).build();
Project project = TestProvisioner.gradleProject(rootFolder());
SpotlessTask task = project.getTasks().create("underTest", SpotlessTask.class);
task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy());
task.setTarget(files);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,18 @@
import org.assertj.core.api.Assertions;
import org.gradle.api.Project;
import org.gradle.api.file.ConfigurableFileTree;
import org.gradle.testfixtures.ProjectBuilder;
import org.junit.Before;
import org.junit.Test;

import com.diffplug.spotless.ResourceHarness;
import com.diffplug.spotless.TestProvisioner;

public class FileTreeTest extends ResourceHarness {
private ConfigurableFileTree fileTree;

@Before
public void fileTree() throws IOException {
Project project = ProjectBuilder.builder()
.withProjectDir(rootFolder())
.build();
Project project = TestProvisioner.gradleProject(rootFolder());
fileTree = project.fileTree(rootFolder());
fileTree.exclude("userHome"); // somehow we're getting userHome\native\19\windows-amd64\native-platform.dll
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@
import org.assertj.core.api.Assertions;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.testfixtures.ProjectBuilder;
import org.junit.Before;
import org.junit.Test;

import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.ResourceHarness;
import com.diffplug.spotless.TestProvisioner;

public class FormatTaskTest extends ResourceHarness {
private SpotlessTask checkTask;
private SpotlessTask applyTask;

@Before
public void createTask() {
Project project = ProjectBuilder.builder().build();
public void createTask() throws IOException {
Project project = TestProvisioner.gradleProject(rootFolder());
checkTask = project.getTasks().create("checkTaskUnderTest", SpotlessTask.class);
checkTask.setCheck();
applyTask = project.getTasks().create("applyTaskUnderTest", SpotlessTask.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

import org.assertj.core.api.Assertions;
import org.gradle.api.Project;
import org.gradle.testfixtures.ProjectBuilder;
import org.junit.Assert;
import org.junit.Test;

Expand All @@ -35,6 +34,7 @@
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.ResourceHarness;
import com.diffplug.spotless.TestProvisioner;

public class PaddedCellTaskTest extends ResourceHarness {
private static final boolean IS_WIN = StandardSystemProperty.OS_NAME.value().toLowerCase(Locale.US).contains("win");
Expand All @@ -44,7 +44,7 @@ private static String slashify(String input) {
}

private class Bundle {
Project project = ProjectBuilder.builder().withProjectDir(rootFolder()).build();
Project project = TestProvisioner.gradleProject(rootFolder());
File file;
SpotlessTask check;
SpotlessTask apply;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@

import org.gradle.api.Project;
import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
import org.gradle.testfixtures.ProjectBuilder;
import org.gradle.testkit.runner.GradleRunner;
import org.junit.Ignore;
import org.junit.Test;

import com.diffplug.common.base.Errors;
import com.diffplug.common.base.StandardSystemProperty;
import com.diffplug.spotless.TestProvisioner;

/**
* If you'd like to step through the full spotless plugin,
Expand Down Expand Up @@ -117,7 +117,7 @@ private static void runTasksManually(Type type) throws Exception {

/** Creates a Project which has had the SpotlessExtension setup. */
private static Project createProject(Consumer<SpotlessExtension> test) throws Exception {
Project project = ProjectBuilder.builder().withProjectDir(new File("").getAbsoluteFile()).build();
Project project = TestProvisioner.gradleProject(new File("").getAbsoluteFile());
// create the spotless plugin
SpotlessPlugin plugin = project.getPlugins().apply(SpotlessPlugin.class);
// setup the plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

import com.diffplug.common.base.Errors;
import com.diffplug.common.io.Resources;

public class ResourceHarness {
Expand Down Expand Up @@ -77,8 +78,8 @@ private void logFailure(String message, Description description) {
};

/** Returns the root folder (canonicalized to fix OS X issue) */
protected File rootFolder() throws IOException {
return folderDontUseDirectly.getRoot().getCanonicalFile();
protected File rootFolder() {
return Errors.rethrow().get(() -> folderDontUseDirectly.getRoot().getCanonicalFile());
}

/** Returns a new child of the root folder. */
Expand Down
89 changes: 55 additions & 34 deletions testlib/src/main/java/com/diffplug/spotless/TestProvisioner.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
Expand All @@ -31,14 +33,21 @@
import org.gradle.api.artifacts.dsl.RepositoryHandler;
import org.gradle.testfixtures.ProjectBuilder;

import com.diffplug.common.base.Box;
import com.diffplug.common.base.Errors;
import com.diffplug.common.base.StandardSystemProperty;
import com.diffplug.common.base.Suppliers;
import com.diffplug.common.collect.ImmutableSet;
import com.diffplug.common.io.Files;

public class TestProvisioner {
public static Project gradleProject(File dir) {
File userHome = new File(StandardSystemProperty.USER_HOME.value());
return ProjectBuilder.builder()
.withGradleUserHomeDir(new File(userHome, ".gradle"))
.withProjectDir(dir)
.build();
}

/**
* Creates a Provisioner for the given repositories.
*
Expand All @@ -47,26 +56,35 @@ public class TestProvisioner {
*
* Every call to resolve will take about 1 second, even when all artifacts are resolved.
*/
private static Supplier<Provisioner> createLazyWithRepositories(Consumer<RepositoryHandler> repoConfig) {
private static Provisioner createWithRepositories(Consumer<RepositoryHandler> repoConfig) {
// Running this takes ~3 seconds the first time it is called. Probably because of classloading.
return Suppliers.memoize(() -> {
Project project = ProjectBuilder.builder().build();
repoConfig.accept(project.getRepositories());
return (withTransitives, mavenCoords) -> {
Dependency[] deps = mavenCoords.stream()
.map(project.getDependencies()::create)
.toArray(Dependency[]::new);
Configuration config = project.getConfigurations().detachedConfiguration(deps);
config.setTransitive(withTransitives);
config.setDescription(mavenCoords.toString());
File tempDir = Files.createTempDir();
Project project = TestProvisioner.gradleProject(tempDir);
repoConfig.accept(project.getRepositories());
return (withTransitives, mavenCoords) -> {
Dependency[] deps = mavenCoords.stream()
.map(project.getDependencies()::create)
.toArray(Dependency[]::new);
Configuration config = project.getConfigurations().detachedConfiguration(deps);
config.setTransitive(withTransitives);
config.setDescription(mavenCoords.toString());
try {
return config.resolve();
} catch (ResolveException e) {
/* Provide Maven coordinates in exception message instead of static string 'detachedConfiguration' */
throw new ResolveException(config.getDescription(), e);
} finally {
// delete the temp dir
try {
return config.resolve();
} catch (ResolveException e) {
/* Provide Maven coordinates in exception message instead of static string 'detachedConfiguration' */
throw new ResolveException(config.getDescription(), e);
java.nio.file.Files.walk(tempDir.toPath())
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (IOException e) {
throw Errors.asRuntime(e);
}
};
});
}
};
}

/** Creates a Provisioner which will cache the result of previous calls. */
Expand All @@ -86,20 +104,23 @@ private static Provisioner caching(String name, Supplier<Provisioner> input) {
} else {
cached = new HashMap<>();
}
return (withTransitives, mavenCoords) -> {
Box<Boolean> wasChanged = Box.of(false);
ImmutableSet<File> result = cached.computeIfAbsent(ImmutableSet.copyOf(mavenCoords), coords -> {
wasChanged.set(true);
return ImmutableSet.copyOf(input.get().provisionWithTransitives(withTransitives, coords));
});
if (wasChanged.get()) {
try (ObjectOutputStream outputStream = new ObjectOutputStream(Files.asByteSink(cacheFile).openBufferedStream())) {
outputStream.writeObject(cached);
} catch (IOException e) {
throw Errors.asRuntime(e);
return (withTransitives, mavenCoordsRaw) -> {
ImmutableSet<String> mavenCoords = ImmutableSet.copyOf(mavenCoordsRaw);
synchronized (TestProvisioner.class) {
ImmutableSet<File> result = cached.get(mavenCoords);
// double-check that depcache pruning hasn't removed them since our cache cached them
boolean needsToBeSet = result == null || !result.stream().allMatch(file -> file.exists() && file.isFile() && file.length() > 0);
if (needsToBeSet) {
result = ImmutableSet.copyOf(input.get().provisionWithTransitives(withTransitives, mavenCoords));
cached.put(mavenCoords, result);
try (ObjectOutputStream outputStream = new ObjectOutputStream(Files.asByteSink(cacheFile).openBufferedStream())) {
outputStream.writeObject(cached);
} catch (IOException e) {
throw Errors.asRuntime(e);
}
}
return result;
}
return result;
};
}

Expand All @@ -109,7 +130,7 @@ public static Provisioner jcenter() {
}

private static final Supplier<Provisioner> jcenter = Suppliers.memoize(() -> {
return caching("jcenter", createLazyWithRepositories(repo -> repo.jcenter()));
return caching("jcenter", () -> createWithRepositories(repo -> repo.jcenter()));
});

/** Creates a Provisioner for the mavenCentral repo. */
Expand All @@ -118,22 +139,22 @@ public static Provisioner mavenCentral() {
}

private static final Supplier<Provisioner> mavenCentral = Suppliers.memoize(() -> {
return caching("mavenCentral", createLazyWithRepositories(repo -> repo.mavenCentral()));
return caching("mavenCentral", () -> createWithRepositories(repo -> repo.mavenCentral()));
});

/** Creates a Provisioner for the local maven repo for development purpose. */
public static Provisioner mavenLocal() {
return mavenLocal.get();
}

private static final Supplier<Provisioner> mavenLocal = createLazyWithRepositories(repo -> repo.mavenLocal());
private static final Supplier<Provisioner> mavenLocal = () -> createWithRepositories(repo -> repo.mavenLocal());

/** Creates a Provisioner for the Sonatype snapshots maven repo for development purpose. */
public static Provisioner snapshots() {
return snapshots.get();
}

private static final Supplier<Provisioner> snapshots = createLazyWithRepositories(repo -> {
private static final Supplier<Provisioner> snapshots = () -> createWithRepositories(repo -> {
repo.maven(setup -> {
setup.setUrl("https://oss.sonatype.org/content/repositories/snapshots");
});
Expand Down