Skip to content

Commit

Permalink
implementing "parallelism-safe" Yatspec results (squashed commit for …
Browse files Browse the repository at this point in the history
…upstream PR)
  • Loading branch information
pobrelkey authored and pobrelkey committed Jul 2, 2021
1 parent 1249a55 commit a3654ae
Show file tree
Hide file tree
Showing 23 changed files with 598 additions and 92 deletions.
2 changes: 1 addition & 1 deletion build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
</path>

<target name="update">
<install version="89"/>
<install version="2.33"/>
<parallel>
<update dependencies="build/build.dependencies"
directory="${build.dependencies.dir}"/>
Expand Down
2 changes: 1 addition & 1 deletion build/macros.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

<sequential>
<mkdir dir="@{classes}"/>
<javac srcdir="@{src}" destdir="@{classes}" classpathref="@{classpathref}" target="1.5" debug="true" includeantruntime="false"/>
<javac srcdir="@{src}" destdir="@{classes}" classpathref="@{classpathref}" source="8" target="8" debug="true" includeantruntime="false"/>

<copy todir="@{classes}">
<fileset dir="@{src}">
Expand Down
2 changes: 1 addition & 1 deletion build/shavenmaven.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<sequential>
<mkdir dir="@{directory}"/>
<get src="http://repo.bodar.com/com/googlecode/shavenmaven/shavenmaven/@{version}/shavenmaven-@{version}.jar"
<get src="https://github.com/bodar/shavenmaven/releases/download/@{version}/shavenmaven-@{version}.jar"
dest="@{directory}/shavenmaven.jar" usetimestamp="true"/>
</sequential>
</macrodef>
Expand Down
Binary file modified lib/shavenmaven.jar
Binary file not shown.
16 changes: 16 additions & 0 deletions src/com/googlecode/yatspec/junit/SpecResultMetadataListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.googlecode.yatspec.junit;

import com.googlecode.yatspec.state.Result;
import com.googlecode.yatspec.state.ResultMetadata;
import com.googlecode.yatspec.state.ResultMetadataOnly;

import java.io.File;
import java.util.Collection;
import java.util.Collections;

public interface SpecResultMetadataListener extends SpecResultListener {
void completeMetadata(File yatspecOutputDir, Collection<ResultMetadata> results) throws Exception;
default void complete(File yatspecOutputDir, Result result) throws Exception {
completeMetadata(yatspecOutputDir, Collections.singleton(ResultMetadataOnly.fromResult(result)));
}
}
98 changes: 80 additions & 18 deletions src/com/googlecode/yatspec/junit/SpecRunner.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.googlecode.yatspec.junit;

import com.googlecode.totallylazy.Predicate;
import com.googlecode.yatspec.state.Result;
import com.googlecode.yatspec.state.Scenario;
import com.googlecode.yatspec.state.TestResult;
import com.googlecode.yatspec.state.*;
import com.googlecode.yatspec.state.givenwhenthen.TestState;
import com.googlecode.yatspec.state.givenwhenthen.WithTestState;
import org.junit.runner.notification.Failure;
Expand All @@ -12,16 +9,19 @@
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static com.googlecode.totallylazy.Sequences.sequence;
import static java.lang.System.getProperty;

public class SpecRunner extends TableRunner {
public static final String OUTPUT_DIR = "yatspec.output.dir";
public static final String DATA_DIR = "yatspec.data.dir";
private static final Set<String> resultsAlreadySeen = new HashSet<>();
public static final String SERIALIZED_DATA_EXTENSION = ".ser";
private final Result testResult;
private Map<String, Scenario> currentScenario = new HashMap<String, Scenario>();

Expand All @@ -32,15 +32,7 @@ public SpecRunner(Class<?> klass) throws org.junit.runners.model.InitializationE

@Override
protected List<FrameworkMethod> computeTestMethods() {
return sequence(super.computeTestMethods()).filter(isNotEvaluateMethod()).toList();
}

private static Predicate<FrameworkMethod> isNotEvaluateMethod() {
return new Predicate<FrameworkMethod>() {
public boolean matches(FrameworkMethod method) {
return !method.getName().equals("evaluate");
}
};
return super.computeTestMethods().stream().filter(method -> !method.getName().equals("evaluate")).collect(Collectors.toList());
}

private WithCustomResultListeners listeners = new DefaultResultListeners();
Expand All @@ -62,8 +54,20 @@ public void run(RunNotifier notifier) {
notifier.addListener(listener);
super.run(notifier);
notifier.removeListener(listener);

Iterable<SpecResultListener> resultListeners;
try {
for (SpecResultListener resultListener : listeners.getResultListeners()) {
resultListeners = listeners.getResultListeners();
} catch (Exception e) {
throw new RuntimeException(e);
}

if (dataDirectory() != null) {
updateTestResults(dataDirectory(), testResult, resultListeners);
}

try {
for (SpecResultListener resultListener : resultListeners) {
resultListener.complete(outputDirectory(), testResult);
}
} catch (Exception e) {
Expand All @@ -72,10 +76,68 @@ public void run(RunNotifier notifier) {
}
}

private static void updateTestResults(File dataDirectory, Result result, Iterable<SpecResultListener> resultListeners) {
ResultMetadataOnly resultMetadataOnly = ResultMetadataOnly.fromResult(result);
String testClassName = resultMetadataOnly.getTestClassName();
resultsAlreadySeen.add(testClassName);

{
dataDirectory.mkdirs();
File tmpFile = new File(dataDirectory, testClassName + SERIALIZED_DATA_EXTENSION + ".tmp");
try (FileOutputStream fos = new FileOutputStream(tmpFile); ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(resultMetadataOnly);
} catch (IOException e) {
throw new RuntimeException("error serializing Yatspec test data: " + e.getStackTrace(), e);
}
File finalFile = new File(dataDirectory, testClassName + SERIALIZED_DATA_EXTENSION);
tmpFile.renameTo(finalFile);
}

Set<SpecResultListener> metadataListeners = StreamSupport.stream(resultListeners.spliterator(), false).filter(x -> x instanceof SpecResultMetadataListener).collect(Collectors.toSet());
if (!metadataListeners.isEmpty()) {
Set<ResultMetadata> metadata = new HashSet<>();
for (File file : dataDirectory.listFiles()) {
String fileName = file.getName();
if (!fileName.endsWith(SERIALIZED_DATA_EXTENSION)) {
continue;
}
String className = fileName.substring(0, fileName.length() - SERIALIZED_DATA_EXTENSION.length());
if (resultsAlreadySeen.contains(className)) {
continue;
}

try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) {
metadata.add((ResultMetadata) ois.readObject());
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("error deserializing Yatspec test data: " + e.getStackTrace(), e);
}
}
try {
for (SpecResultListener metadataListener : metadataListeners) {
((SpecResultMetadataListener) metadataListener).completeMetadata(outputDirectory(), metadata);
}
} catch (Exception e) {
System.out.println("Error while writing yatspec output");
e.printStackTrace(System.out);
}

resultsAlreadySeen.addAll(metadata.stream().map(ResultMetadata::getTestClassName).collect(Collectors.toSet()));
}
}

public static File outputDirectory() {
return new File(getProperty(OUTPUT_DIR, getProperty("java.io.tmpdir")));
}

private static File dataDirectory() {
String dataDirPath = getProperty(DATA_DIR);
if (dataDirPath == null || dataDirPath.length() == 0) {
return null;
} else {
return new File(dataDirPath);
}
}

@Override
protected Statement methodInvoker(final FrameworkMethod method, final Object test) {
final Statement statement = super.methodInvoker(method, test);
Expand Down
11 changes: 6 additions & 5 deletions src/com/googlecode/yatspec/rendering/Index.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package com.googlecode.yatspec.rendering;

import com.googlecode.totallylazy.Sequence;
import com.googlecode.yatspec.state.Result;
import com.googlecode.yatspec.state.ResultMetadata;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import static com.googlecode.totallylazy.Sequences.sequence;

public class Index {
private final List<Result> files = new CopyOnWriteArrayList<Result>();
private final List<ResultMetadata> files = new CopyOnWriteArrayList<ResultMetadata>();

public Index add(Result result) {
files.add(result);
public Index addAll(Collection<ResultMetadata> results) {
files.addAll(results);
return this;
}

public Sequence<Result> entries() {
public Sequence<ResultMetadata> entries() {
return sequence(files);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import com.googlecode.yatspec.state.Result;
import com.googlecode.yatspec.state.ScenarioTableHeader;
import com.googlecode.yatspec.state.Status;
import com.googlecode.yatspec.state.TestMethod;
import com.googlecode.yatspec.state.TestMethodMetadata;
import org.antlr.stringtemplate.NoIndentWriter;
import org.antlr.stringtemplate.StringTemplate;

Expand All @@ -25,11 +25,10 @@
import java.util.*;

import static com.googlecode.totallylazy.Callables.asString;
import static com.googlecode.totallylazy.Predicates.always;
import static com.googlecode.totallylazy.Predicates.instanceOf;
import static com.googlecode.totallylazy.Predicates.not;
import static com.googlecode.totallylazy.Predicates.*;
import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.yatspec.parsing.Files.overwrite;
import static com.googlecode.yatspec.parsing.Files.replaceDotsWithSlashes;
import static com.googlecode.yatspec.rendering.Renderers.registerRenderer;
import static java.lang.String.format;

Expand Down Expand Up @@ -109,13 +108,17 @@ public static String htmlResultRelativePath(Class resultClass) {
return Files.toPath(resultClass) + ".html";
}

public static String htmlResultRelativePathForClassName(String className) {
return replaceDotsWithSlashes(className) + ".html";
}

public static File htmlResultFile(File outputDirectory, Class resultClass) {
return new File(outputDirectory, htmlResultRelativePath(resultClass));
}

public static String testMethodRelativePath(TestMethod testMethod) {
public static String testMethodRelativePath(TestMethodMetadata testMethod) {
return format("%s#%s",
htmlResultRelativePath(testMethod.getTestClass()),
htmlResultRelativePathForClassName(testMethod.getTestClassName()),
testMethod.getName());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
package com.googlecode.yatspec.rendering.html.index;

import com.googlecode.funclate.stringtemplate.EnhancedStringTemplateGroup;
import com.googlecode.yatspec.junit.SpecResultListener;
import com.googlecode.yatspec.junit.SpecResultMetadataListener;
import com.googlecode.yatspec.rendering.Content;
import com.googlecode.yatspec.rendering.ContentAtUrl;
import com.googlecode.yatspec.rendering.Index;
import com.googlecode.yatspec.rendering.html.HtmlResultRenderer;
import com.googlecode.yatspec.state.Result;
import com.googlecode.yatspec.state.ResultMetadata;
import org.antlr.stringtemplate.StringTemplate;

import java.io.File;
import java.util.Collection;

import static com.googlecode.funclate.Model.mutable.model;
import static com.googlecode.yatspec.parsing.Files.overwrite;
import static com.googlecode.yatspec.rendering.html.HtmlResultRenderer.getCssMap;

public class HtmlIndexRenderer implements SpecResultListener {
public class HtmlIndexRenderer implements SpecResultMetadataListener {
private final static Index index = new Index();

@Override
public void complete(File yatspecOutputDir, Result result) throws Exception {
index.add(result);
public void completeMetadata(File yatspecOutputDir, Collection<ResultMetadata> results) throws Exception {
index.addAll(results);
overwrite(outputFile(yatspecOutputDir), render(yatspecOutputDir, index));
}

Expand Down
24 changes: 12 additions & 12 deletions src/com/googlecode/yatspec/rendering/html/index/IndexModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import com.googlecode.totallylazy.Callable1;
import com.googlecode.totallylazy.Sequence;
import com.googlecode.yatspec.rendering.Index;
import com.googlecode.yatspec.state.Result;
import com.googlecode.yatspec.state.ResultMetadata;
import com.googlecode.yatspec.state.Status;
import com.googlecode.yatspec.state.TestMethod;
import com.googlecode.yatspec.state.TestMethodMetadata;

import java.io.File;

Expand All @@ -19,14 +19,14 @@
import static com.googlecode.totallylazy.Strings.startsWith;
import static com.googlecode.yatspec.parsing.Text.wordify;
import static com.googlecode.yatspec.rendering.PackageNames.*;
import static com.googlecode.yatspec.rendering.html.HtmlResultRenderer.htmlResultRelativePath;
import static com.googlecode.yatspec.rendering.html.HtmlResultRenderer.htmlResultRelativePathForClassName;
import static com.googlecode.yatspec.rendering.html.HtmlResultRenderer.testMethodRelativePath;
import static com.googlecode.yatspec.state.Results.packageName;
import static com.googlecode.yatspec.state.Results.resultStatus;
import static com.googlecode.yatspec.state.StatusPriority.statusPriority;

public class IndexModel {
private final Sequence<Result> entries;
private final Sequence<ResultMetadata> entries;
private final Sequence<String> packageNames;
private final File yatspecOutputDir;

Expand Down Expand Up @@ -59,15 +59,15 @@ private Model modelOfPackage(String name) throws Exception {
toList());
}

private Callable1<? super Result, Model> modelOfResult() {
return new Callable1<Result, Model>() {
private Callable1<? super ResultMetadata, Model> modelOfResult() {
return new Callable1<ResultMetadata, Model>() {
@Override
public Model call(Result result) throws Exception {
public Model call(ResultMetadata result) throws Exception {
return model().
add("name", result.getName()).
add("url", htmlResultRelativePath(result.getTestClass())).
add("url", htmlResultRelativePathForClassName(result.getTestClassName())).
add("status", some(result).map(resultStatus()).get()).
add("methods", sequence(result.getTestMethods()).
add("methods", sequence(result.getTestMethodMetadata()).
map(testMethodModel()).
toList());
}
Expand All @@ -83,10 +83,10 @@ private Status statusOfPackage(String name) throws Exception {
getOrElse(Status.Passed);
}

private Callable1<? super TestMethod, Model> testMethodModel() {
return new Callable1<TestMethod, Model>() {
private Callable1<? super TestMethodMetadata, Model> testMethodModel() {
return new Callable1<TestMethodMetadata, Model>() {
@Override
public Model call(TestMethod testMethod) throws Exception {
public Model call(TestMethodMetadata testMethod) throws Exception {
return model().
add("name", testMethod.getDisplayName()).
add("url", testMethodRelativePath(testMethod)).
Expand Down
Loading

0 comments on commit a3654ae

Please sign in to comment.