Skip to content

Commit

Permalink
Add FilePosition to ClassSource, if possible. Closes #196 (#197)
Browse files Browse the repository at this point in the history
This change tries to parse the Test Source file and add the Position of
the Assertion Field to the ClassSource. To locate the source file, this
makes some assumptions about the direcory structure and it may not work
with non-standard directory names and/or trees.
  • Loading branch information
dmfs authored Sep 28, 2024
1 parent 450aeba commit 5c6edb3
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/**
* A {@link ThrowingFunction} to {@link Function} adapter that always returns a result, even in case a {@link Throwable} was thrown.
*/
public final class FailSafe<Argument, Result> implements Function<Argument, Result>
public final class FailSafe<Argument, Result> implements Function<Argument, Result>, java.util.function.Function<Argument, Result>
{
private final ThrowingFunction<Argument, Result> delegate;
private final Function<Throwable, Result> fallback;
Expand All @@ -52,4 +52,10 @@ public Result value(Argument argument)
return fallback.value(e);
}
}

@Override
public Result apply(Argument argument)
{
return value(argument);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.saynotobugs.confidence.quality.object.EqualTo;

import static org.saynotobugs.confidence.Assertion.assertThat;
import static org.saynotobugs.confidence.core.quality.Function.maps;
import static org.saynotobugs.confidence.core.quality.Grammar.to;


class FailSafeTest
Expand All @@ -43,4 +45,21 @@ void testException()
}),
new Has<>(fs -> fs.value("123"), new EqualTo<>("xyz")));
}

@Test
void testJavaFunctionNoException()
{
assertThat(new FailSafe<String, String>(t -> "fail", x -> x),
maps("123", to("123")));
}


@Test
void testJavaFunctionException()
{
assertThat(new FailSafe<String, String>(Throwable::getMessage, x -> {
throw new IllegalArgumentException("xyz");
}),
maps("123", to("xyz")));
}
}
1 change: 1 addition & 0 deletions confidence-incubator/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {
implementation libs.junit.jupiter.engine
implementation project(':confidence-core')
implementation libs.jems2.confidence
implementation 'com.github.javaparser:javaparser-core:3.26.1'

testImplementation project(':confidence-test')
testImplementation libs.jems2.testing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

package org.saynotobugs.confidence.junit5.engine.testengine.testdescriptor;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import org.junit.platform.engine.EngineExecutionListener;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.engine.TestExecutionResult;
Expand All @@ -26,8 +28,16 @@
import org.junit.platform.engine.support.descriptor.ClassSource;
import org.saynotobugs.confidence.junit5.engine.Assertion;
import org.saynotobugs.confidence.junit5.engine.testengine.Testable;
import org.saynotobugs.confidence.utils.FailSafe;

import java.nio.file.Files;
import java.nio.file.Path;
import java.security.CodeSource;
import java.util.Arrays;
import java.util.Optional;

import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;


/**
Expand All @@ -43,7 +53,7 @@ public ClassTestDescriptor(UniqueId uniqueId, Class<?> javaClass)

Arrays.stream(javaClass.getDeclaredFields())
.filter(field -> Assertion.class.isAssignableFrom(field.getType()))
.map(field -> new FieldTestDescriptor(getUniqueId(), javaClass, field))
.map(field -> new FieldTestDescriptor(getUniqueId(), javaClass, testSources(javaClass), field))
.forEach(this::addChild);
}

Expand Down Expand Up @@ -73,4 +83,37 @@ public Type getType()
{
return Type.CONTAINER;
}


private static Optional<CompilationUnit> testSources(Class<?> clazz)
{
return gradleTestSourcePath(clazz)
.or(() -> naiveTestSourcePath(clazz))
.flatMap(new FailSafe<>(any -> empty(), path -> new JavaParser().parse(path).getResult()));
}

private static Optional<Path> naiveTestSourcePath(Class<?> testClazz)
{
return Optional.of(Path.of("src", "main")
.resolve(Path.of("java", testClazz.getPackage().getName().split("\\.")))
.resolve(testClazz.getSimpleName() + ".java"))
.filter(Files::isRegularFile);
}

private static Optional<Path> gradleTestSourcePath(Class<?> testClazz)
{
return ofNullable(System.getProperty("user.dir"))
.map(Path::of)
.flatMap(workingDir ->
Optional.ofNullable(testClazz.getProtectionDomain().getCodeSource())
.map(CodeSource::getLocation)
.flatMap(new FailSafe<>(any -> empty(), url -> Optional.of(url.toURI())))
.map(source -> workingDir.relativize(Path.of(source)))
.map(relativeClassPath ->
workingDir.resolve(Path.of("src", relativeClassPath.getName(3).toString())
.resolve(Path.of(relativeClassPath.getName(2).toString(), testClazz.getPackageName().split("\\.")))
.resolve(testClazz.getSimpleName() + ".java")))
.filter(Files::isRegularFile));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@

package org.saynotobugs.confidence.junit5.engine.testengine.testdescriptor;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.FieldDeclaration;
import org.junit.platform.engine.EngineExecutionListener;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.engine.UniqueId;
import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;
import org.junit.platform.engine.support.descriptor.ClassSource;
import org.junit.platform.engine.support.descriptor.FilePosition;
import org.saynotobugs.confidence.junit5.engine.Assertion;
import org.saynotobugs.confidence.junit5.engine.testengine.Testable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;


/**
Expand All @@ -40,11 +44,11 @@ public final class FieldTestDescriptor extends AbstractTestDescriptor implements
private final Field javaField;


public FieldTestDescriptor(UniqueId uniqueId, Class<?> javaClass, Field javaField)
public FieldTestDescriptor(UniqueId uniqueId, Class<?> javaClass, Optional<CompilationUnit> testSource, Field javaField)
{
super(uniqueId.append("field", javaField.getName()),
(javaField.getName().replace("_", " ")).trim(),
ClassSource.from(javaClass));
ClassSource.from(javaClass, getFieldLineNumber(testSource, javaField.getName())));
this.javaClass = javaClass;
this.javaField = javaField;
}
Expand Down Expand Up @@ -73,4 +77,16 @@ public Type getType()
{
return Type.TEST;
}


private static FilePosition getFieldLineNumber(Optional<CompilationUnit> compilationUnit, String fieldName)
{
return compilationUnit.flatMap(cu -> cu.findAll(FieldDeclaration.class)
.stream().flatMap(fieldDeclaration -> fieldDeclaration.getVariables().stream())
.filter(node -> fieldName.equals(node.getName().asString()))
.flatMap(node -> node.getBegin().stream())
.map(begin -> FilePosition.from(begin.line, begin.column))
.findFirst())
.orElse(null);
}
}

0 comments on commit 5c6edb3

Please sign in to comment.