Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 199 additions & 50 deletions src/main/java/edu/hm/hafner/util/Ensure.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
package edu.hm.hafner.util;

import org.apache.commons.lang3.StringUtils;

import com.google.errorprone.annotations.FormatMethod;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.CheckReturnValue;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Formatter;
import java.util.List;
import java.util.Objects;

/**
* Provides several helper methods to validate method arguments and class invariants, thus supporting the design by
* contract concept (DBC).
* Provides several helper methods to validate method arguments and class invariants, thus supporting the
* design-by-contract concept (DBC).
*
* <p>
* Note: the static methods provided by this class use a fluent interface, i.e., in order to verify an assertion a
* method sequence needs to be called.
* Note: the static methods provided by this class use a fluent interface, i.e., to verify an assertion, a method
* sequence needs to be called.
* </p>
*
* <p>
* Available checks:
* Available checks:
* </p>
* <ul>
* <li>Boolean assertions, e.g., {@code Ensure.that(condition).isTrue(); } </li>
Expand All @@ -33,7 +35,8 @@
* </ul>
*
* @author Ullrich Hafner
* @see <a href="http://se.ethz.ch/~meyer/publications/computer/contract.pdf"> Design by Contract (Meyer, Bertrand)</a>
* @see <a href="http://se.ethz.ch/~meyer/publications/computer/contract.pdf"> Design by Contract (Meyer,
* Bertrand)</a>
*/
public final class Ensure {
/**
Expand Down Expand Up @@ -172,23 +175,6 @@
throw new AssertionError(message.formatted(args));
}

/**
* Throws a {@link NullPointerException} with the specified detail message.
*
* @param message
* a {@link Formatter formatted message} with the description of the error
* @param args
* Arguments referenced by the format specifiers in the formatted message. If there are more arguments than
* format specifiers, the extra arguments are ignored. The number of arguments is variable and may be zero.
*
* @throws AssertionError
* always thrown
*/
@FormatMethod
private static void throwNullPointerException(final String message, final Object... args) {
throw new NullPointerException(message.formatted(args)); // NOPMD
}

private Ensure() {
// prevents instantiation
}
Expand All @@ -212,12 +198,79 @@
* that each element of the iterable is not {@code null}.
*
* @throws AssertionError
* if the iterable is empty (or {@code null}), or at least one iterable element is {@code null}.
* if the iterable is empty (or {@code null}), or at least one iterable element is {@code null}
*/
public void isNotEmpty() {
isNotEmpty("Iterable is empty or NULL");
}

/**
* Ensures that the given iterable is not {@code null} but empty.
*
* @throws AssertionError
* if the iterable is not empty (or {@code null})
*/
public void isEmpty() {
isEmpty("Iterable '%s' is not empty or NULL", renderValue());
}

/**
* Ensures that the given iterable is not {@code null} and contains the specified number of elements.
* Additionally, ensures that each element of the iterable is not {@code null}.
*
* @param expectedSize
* the expected number of elements in the iterable
*
* @throws AssertionError
* if the iterable does not contain the expected number of elements (or is {@code null}), or at least
* one iterable element is {@code null}
*/
public void hasSize(final int expectedSize) {
hasSize(expectedSize,
"Iterable does not contain the expected number of elements. "
+ "Actual value: %s. Expected size: %d",
renderValue(), expectedSize);
}

/**
* Ensures that the given iterable is not {@code null} and contains the specified number of elements.
* Additionally, ensures that each element of the iterable is not {@code null}.
*
* @param expectedSize
* the expected number of elements in the iterable
* @param explanation
* a {@link Formatter formatted message} explaining the assertion
* @param args
* Arguments referenced by the format specifiers in the formatted explanation. If there are more
* arguments than format specifiers, the extra arguments are ignored. The number of arguments is
* variable and may be zero.
*
* @throws AssertionError
* if the iterable does not contain the expected number of elements (or is {@code null}), or at least
* one iterable element is {@code null}
*/
@FormatMethod
public void hasSize(final int expectedSize, final String explanation, final Object... args) {
isNotNull(explanation, args);

Check warning on line 254 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 254 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$IterableCondition::isNotNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

Check warning on line 254 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 254 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$IterableCondition::isNotNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

var size = computeSize(explanation, args);
if (size != expectedSize) {
throwException(explanation, args);

Check warning on line 258 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 258 is not covered by tests

Check warning on line 258 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 258 is not covered by tests
}
}

@FormatMethod
private int computeSize(final String explanation, final Object... args) {
int size = 0;
for (Object object : getValue()) {
if (object == null) {
throwException(explanation, args);

Check warning on line 267 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 267 is not covered by tests

Check warning on line 267 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 267 is not covered by tests
}
size++;
}
return size;
}

/**
* Ensures that the given iterable is not {@code null} and contains at least one element. Additionally, ensures
* that each element of the iterable is not {@code null}.
Expand All @@ -230,20 +283,35 @@
* variable and may be zero.
*
* @throws AssertionError
* if the iterable is empty (or {@code null}), or at least one iterable element is {@code null}.
* if the iterable is empty (or {@code null}), or at least one iterable element is {@code null}
*/
@FormatMethod
public void isNotEmpty(final String explanation, final Object... args) {
isNotNull(explanation, args);

if (getValue().iterator().hasNext()) {
for (Object object : getValue()) {
if (object == null) {
throwException(explanation, args);
}
}
if (computeSize(explanation, args) == 0) {
throwException(explanation, args);

Check warning on line 293 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 293 is not covered by tests

Check warning on line 293 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 293 is not covered by tests
}
else {
}

/**
* Ensures that the given iterable is not {@code null} but has no elements.
*
* @param explanation
* a {@link Formatter formatted message} explaining the assertion
* @param args
* Arguments referenced by the format specifiers in the formatted explanation. If there are more
* arguments than format specifiers, the extra arguments are ignored. The number of arguments is
* variable and may be zero.
*
* @throws AssertionError
* if the iterable is not empty (or {@code null})
*/
@FormatMethod
public void isEmpty(final String explanation, final Object... args) {
isNotNull(explanation, args);

Check warning on line 312 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 312 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$IterableCondition::isNotNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

Check warning on line 312 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 312 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$IterableCondition::isNotNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

if (computeSize(explanation, args) != 0) {
throwException(explanation, args);
}
}
Expand Down Expand Up @@ -278,7 +346,7 @@
* if the collection is {@code null} or if the specified element is not found
*/
public void contains(final Object element) {
contains(element, "Collection %s does not contain element '%s'", getValue(), element);
contains(element, "Collection '%s' does not contain element '%s'", renderValue(), element);
}

/**
Expand Down Expand Up @@ -315,7 +383,7 @@
* if the collection is {@code null} or if the specified element is part of the collection
*/
public void doesNotContain(final Object element) {
doesNotContain(element, "Collection '%s' contains element '%s'", getValue(), element);
doesNotContain(element, "Collection '%s' contains element '%s' but should not", renderValue(), element);
}

/**
Expand Down Expand Up @@ -363,12 +431,62 @@
* that each element of the array is not {@code null}.
*
* @throws AssertionError
* if the array is empty (or {@code null}), or at least one array element is {@code null}.
* if the array is empty (or {@code null}), or at least one array element is {@code null}
*/
public void isNotEmpty() {
isNotEmpty("Array is empty or NULL");
}

/**
* Ensures that the given array is not {@code null} but empty.
*
* @throws AssertionError
* if the array is not empty (or {@code null})
*/
public void isEmpty() {
isEmpty("Array '%s' is not empty or NULL", renderValue());
}

/**
* Ensures that the given array is not {@code null} and has the specified number of elements. Additionally,
* ensures that each element of the array is not {@code null}.
*
* @param expectedSize
* the expected number of elements in the array
*
* @throws AssertionError
* if the array does not contain the expected number of elements (or is {@code null}), or at least one
* array element is {@code null}
*/
public void hasSize(final int expectedSize) {
hasSize(expectedSize,
"Array does not contain the expected number of elements. "
+ "Actual value: %s. Expected size: %d",
renderValue(), expectedSize);
}

/**
* Ensures that the given array is not {@code null} but empty.
*
* @param explanation
* a {@link Formatter formatted message} explaining the assertion
* @param args
* Arguments referenced by the format specifiers in the formatted explanation. If there are more
* arguments than format specifiers, the extra arguments are ignored. The number of arguments is
* variable and may be zero.
*
* @throws AssertionError
* if the array is empty (or {@code null}), or at least one array element is {@code null}.
*/
@FormatMethod
public void isEmpty(final String explanation, final Object... args) {
isNotNull(explanation, args);

Check warning on line 483 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 483 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$ArrayCondition::isNotNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

Check warning on line 483 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 483 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$ArrayCondition::isNotNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

if (getValue().length != 0) {
throwException(explanation, args);

Check warning on line 486 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 486 is not covered by tests

Check warning on line 486 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 486 is not covered by tests
}
}

/**
* Ensures that the given array is not {@code null} and contains at least one element. Additionally, ensures
* that each element of the array is not {@code null}.
Expand All @@ -391,15 +509,46 @@
throwException(explanation, args);
}
else {
for (Object object : getValue()) {
if (object == null) {
throwException(explanation, args);
}
}
doesNotContainNull(explanation, args);
}
}

// FIXME: add isEmpty() methods
/**
* Ensures that the given array is not {@code null} and contains the specified number of elements. Additionally,
* ensures that each element of the array is not {@code null}.
*
* @param expectedSize
* the expected number of elements in the array
* @param explanation
* a {@link Formatter formatted message} explaining the assertion
* @param args
* Arguments referenced by the format specifiers in the formatted explanation. If there are more
* arguments than format specifiers, the extra arguments are ignored. The number of arguments is
* variable and may be zero.
*
* @throws AssertionError
* if the array is empty (or {@code null}), or at least one array element is {@code null}.
*/
@FormatMethod
public void hasSize(final int expectedSize, final String explanation, final Object... args) {
isNotNull(explanation, args);

Check warning on line 534 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 534 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$ArrayCondition::isNotNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

Check warning on line 534 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 534 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$ArrayCondition::isNotNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

if (getValue().length == expectedSize) {

Check warning on line 536 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Partially covered line

Line 536 is only partially covered, one branch is missing

Check warning on line 536 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Partially covered line

Line 536 is only partially covered, one branch is missing
doesNotContainNull(explanation, args);

Check warning on line 537 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 537 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$ArrayCondition::doesNotContainNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

Check warning on line 537 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 537 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/Ensure$ArrayCondition::doesNotContainNull (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)
}
else {
throwException(explanation, args);

Check warning on line 540 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 540 is not covered by tests

Check warning on line 540 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 540 is not covered by tests
}
}

@FormatMethod
private void doesNotContainNull(final String explanation, final Object... args) {
for (Object object : getValue()) {
if (object == null) {
throwException(explanation, args);

Check warning on line 548 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 548 is not covered by tests

Check warning on line 548 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Not covered line

Line 548 is not covered by tests
}
}
}
}

/**
Expand Down Expand Up @@ -556,25 +705,25 @@
*/
@FormatMethod
public void isNotNull(final String explanation, final Object... args) {
var nullPointerException = new NullPointerException(explanation.formatted(args));
if (value == null || additionalValues == null) {
throwNullPointerException(explanation, args);
throw nullPointerException; // NOPMD
}
else {
for (Object additionalValue : additionalValues) {
if (additionalValue == null) {
throwNullPointerException(explanation, args);
throw nullPointerException; // NOPMD
}
}
}
}

@SuppressFBWarnings("NP")
@SuppressWarnings("PMD.AvoidThrowingNullPointerException")
T getValue() {
if (value == null) {
throw new NullPointerException("Value is null");
}
return value;
return Objects.requireNonNull(value, "Value is NULL");
}

String renderValue() {
return value + ", " + StringUtils.join(additionalValues, ", ");

Check warning on line 726 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 726 (EmptyObjectReturnValsMutator)
Raw output
Survived mutations:
- replaced return value with "" for edu/hm/hafner/util/Ensure$ObjectCondition::renderValue (org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator)

Check warning on line 726 in src/main/java/edu/hm/hafner/util/Ensure.java

View workflow job for this annotation

GitHub Actions / Quality Monitor - Quality gates failed

Mutation survived

One mutation survived in line 726 (EmptyObjectReturnValsMutator)
Raw output
Survived mutations:
- replaced return value with "" for edu/hm/hafner/util/Ensure$ObjectCondition::renderValue (org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator)
}

/**
Expand Down
Loading
Loading