Skip to content

Commit

Permalink
Merge pull request #123 from TNG/fix-assertionerror-mem-leak
Browse files Browse the repository at this point in the history
Fixed memory leak in ArchRule.AssertionError in combination with JUnit 4
  • Loading branch information
codecholeric committed Nov 20, 2018
2 parents c8085cb + 8b859a4 commit 83583ee
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@
import com.tngtech.archunit.testutils.ExpectedMethod;
import com.tngtech.archunit.testutils.ExpectedTestFailures;
import com.tngtech.archunit.testutils.MessageAssertionChain;
import com.tngtech.archunit.testutils.ResultStoringExtension;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;

Expand Down Expand Up @@ -139,6 +143,21 @@

class ExamplesIntegrationTest {

@BeforeAll
static void initExtension() {
ResultStoringExtension.enable();
}

@AfterEach
void tearDown() {
ResultStoringExtension.reset();
}

@AfterAll
static void disableExtension() {
ResultStoringExtension.disable();
}

@TestFactory
Stream<DynamicTest> CodingRulesTest() {
return ExpectedTestFailures
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,58 @@
import com.tngtech.archunit.exampletest.extension.ExampleExtension;
import com.tngtech.archunit.lang.ArchRule;
import org.assertj.core.api.Condition;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static com.google.common.collect.Iterables.getOnlyElement;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import static org.assertj.core.api.Assertions.assertThat;

public class ExtensionIntegrationTest {
class ExtensionIntegrationTest {
private final JavaClasses classes = new ClassFileImporter().importPackagesOf(ClassViolatingCodingRules.class);

@Test
public void evaluation_results_are_dispatched_to_extensions() {
@BeforeEach
void setUp() {
ExampleExtension.reset();
try {
ArchConfiguration.get().configureExtension(ExampleExtension.UNIQUE_IDENTIFIER)
.setProperty("enabled", true);
}

@AfterEach
void tearDown() {
ExampleExtension.reset();
ArchConfiguration.get().configureExtension(ExampleExtension.UNIQUE_IDENTIFIER)
.setProperty("enabled", false);
}

ArchRule rule = noClasses().should().haveFullyQualifiedName(ClassViolatingCodingRules.class.getName());
checkRuleAndIgnoreFailure(classes, rule);
@Test
void evaluation_results_are_dispatched_to_extensions() {
ArchConfiguration.get().configureExtension(ExampleExtension.UNIQUE_IDENTIFIER)
.setProperty("enabled", true);

assertThat(ExampleExtension.getConfigurationEvents()).hasSize(1);
assertThat(ExampleExtension.getConfigurationEvents())
.extracting("properties").are(containingEntry("example-prop", "exampleValue"));
ArchRule rule = noClasses().should().haveFullyQualifiedName(ClassViolatingCodingRules.class.getName());
checkRuleAndIgnoreFailure(classes, rule);

EvaluatedRuleEvent event = getOnlyElement(ExampleExtension.getEvaluatedRuleEvents());
assertThat(event.contains(rule)).as("Rule was passed").isTrue();
assertThat(event.contains(classes)).as("Classes were passed").isTrue();
assertThat(event.hasViolationFor(ClassViolatingCodingRules.class))
.as("Has violation for " + ClassViolatingCodingRules.class.getSimpleName()).isTrue();
} finally {
ArchConfiguration.get().configureExtension(ExampleExtension.UNIQUE_IDENTIFIER).setProperty("enabled", false);
}
assertThat(ExampleExtension.getConfigurationEvents()).hasSize(1);
assertThat(ExampleExtension.getConfigurationEvents())
.extracting("properties").are(containingEntry("example-prop", "exampleValue"));

EvaluatedRuleEvent event = getOnlyElement(ExampleExtension.getEvaluatedRuleEvents());
assertThat(event.contains(rule)).as("Rule was passed").isTrue();
assertThat(event.contains(classes)).as("Classes were passed").isTrue();
assertThat(event.hasViolationFor(ClassViolatingCodingRules.class))
.as("Has violation for " + ClassViolatingCodingRules.class.getSimpleName()).isTrue();
}

@Test
public void evaluation_results_are_only_dispatched_to_enabled_extensions() {
ExampleExtension.reset();
try {
ArchConfiguration.get().configureExtension(ExampleExtension.UNIQUE_IDENTIFIER)
.setProperty("enabled", false);
void evaluation_results_are_only_dispatched_to_enabled_extensions() {
ArchConfiguration.get().configureExtension(ExampleExtension.UNIQUE_IDENTIFIER)
.setProperty("enabled", false);

ArchRule rule = noClasses().should().haveFullyQualifiedName(ClassViolatingCodingRules.class.getName());
checkRuleAndIgnoreFailure(classes, rule);
ArchRule rule = noClasses().should().haveFullyQualifiedName(ClassViolatingCodingRules.class.getName());
checkRuleAndIgnoreFailure(classes, rule);

assertThat(ExampleExtension.getConfigurationEvents()).isEmpty();
assertThat(ExampleExtension.getEvaluatedRuleEvents()).isEmpty();
} finally {
ArchConfiguration.get().configureExtension(ExampleExtension.UNIQUE_IDENTIFIER).setProperty("enabled", false);
}
assertThat(ExampleExtension.getConfigurationEvents()).isEmpty();
assertThat(ExampleExtension.getEvaluatedRuleEvents()).isEmpty();
}

private static Condition<Object> containingEntry(final String propKey, final String propValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.EvaluationResult;
import org.junit.jupiter.api.DynamicTest;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.JUnitCore;
Expand Down Expand Up @@ -259,8 +259,8 @@ private TestFailure(Failure junitFailure, Throwable error) {
this.error = error;
}

ArchRule.AssertionError getAssertionError() {
return (ArchRule.AssertionError) error;
AssertionError getAssertionError() {
return (AssertionError) error;
}

@Override
Expand All @@ -281,13 +281,13 @@ private TestFailures(Iterable<TestFailure> failures) {

void assertNoUnexpectedErrorType() {
List<TestFailure> unexpectedFailureTypes = failures.stream()
.filter(f -> !(f.error instanceof ArchRule.AssertionError))
.filter(f -> !(f.error instanceof AssertionError))
.collect(toList());

if (!unexpectedFailureTypes.isEmpty()) {
throw new AssertionError(String.format(
"Some failures were due to an unexpected error (i.e. not %s): %s",
ArchRule.AssertionError.class.getName(),
AssertionError.class.getName(),
unexpectedFailureTypes));
}
}
Expand Down Expand Up @@ -358,9 +358,10 @@ ExpectedViolationToAssign copy() {
return new ExpectedViolationToAssign(failurePredicate, expectedViolation.copy(), handlingAssertion.copy());
}

Result evaluate(ArchRule.AssertionError error) {
Result evaluate(AssertionError error) {
ViolationComparisonResult violationResult = expectedViolation.evaluate(error);
HandlingAssertion.Result handlingResult = handlingAssertion.evaluate(error.getResult());
EvaluationResult evaluationResult = ResultStoringExtension.getEvaluationResultFor(error.getMessage());
HandlingAssertion.Result handlingResult = handlingAssertion.evaluate(evaluationResult);
return new Result(violationResult, handlingResult);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.tngtech.archunit.testutils;

import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import com.tngtech.archunit.ArchConfiguration;
import com.tngtech.archunit.lang.EvaluationResult;
import com.tngtech.archunit.lang.extension.ArchUnitExtension;
import com.tngtech.archunit.lang.extension.EvaluatedRule;

import static com.google.common.base.Preconditions.checkNotNull;

public class ResultStoringExtension implements ArchUnitExtension {
private static final String UNIQUE_IDENTIFIER = "archunit-result-storing-extension";

private static final ConcurrentHashMap<String, EvaluationResult> storedResults = new ConcurrentHashMap<>();

@Override
public String getUniqueIdentifier() {
return UNIQUE_IDENTIFIER;
}

@Override
public void configure(Properties properties) {
}

@Override
public void handle(EvaluatedRule evaluatedRule) {
storedResults.put(evaluatedRule.getResult().getFailureReport().toString(), evaluatedRule.getResult());
}

public static void reset() {
storedResults.clear();
}

static EvaluationResult getEvaluationResultFor(String errorMessage) {
return checkNotNull(storedResults.get(errorMessage), "No result was recorded for error message: %s", errorMessage);
}

public static void enable() {
ArchConfiguration.get().configureExtension(UNIQUE_IDENTIFIER).setProperty("enabled", true);
}

public static void disable() {
ArchConfiguration.get().configureExtension(UNIQUE_IDENTIFIER).setProperty("enabled", false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.tngtech.archunit.testutils.ResultStoringExtension
17 changes: 1 addition & 16 deletions archunit/src/main/java/com/tngtech/archunit/lang/ArchRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ public static void assertNoViolation(EvaluationResult result) {
Set<Pattern> patterns = readPatternsFrom(ARCHUNIT_IGNORE_PATTERNS_FILE_NAME);
report = report.filter(notMatchedByAny(patterns));
if (!report.isEmpty()) {
String message = report.toString();
throw new AssertionError(message, result);
throw new AssertionError(report.toString());
}
}

Expand Down Expand Up @@ -261,18 +260,4 @@ public String toString() {
}
}
}

final class AssertionError extends java.lang.AssertionError {
private final EvaluationResult result;

private AssertionError(String message, EvaluationResult result) {
super(message);
this.result = result;
}

@PublicAPI(usage = ACCESS)
public EvaluationResult getResult() {
return result;
}
}
}

0 comments on commit 83583ee

Please sign in to comment.