Skip to content

Commit

Permalink
containsAllOf Quality, closes #159 (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmfs authored Dec 30, 2023
1 parent be906bc commit 7a24f7d
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ void test()
assertThat(new ClassThat(new HasDeclaredAnnotations(new Contains<>(new Annotation(TestAnnotation.class)))),
new AllOf<>(
new Passes<Class<?>>(AnnotatedTestClass.class),
new Fails<>(TestClassWithoutAnnotation.class, "Class that had annotations [ ] did not contain { instance of <interface org.saynotobugs.confidence.asm.quality.testclasses.TestAnnotation> }"),
new HasDescription("Class that has annotations contains { instance of <interface org.saynotobugs.confidence.asm.quality.testclasses.TestAnnotation> }")
new Fails<>(TestClassWithoutAnnotation.class, "Class that had annotations [ ] did not contain instance of <interface org.saynotobugs.confidence.asm.quality.testclasses.TestAnnotation>"),
new HasDescription("Class that has annotations contains instance of <interface org.saynotobugs.confidence.asm.quality.testclasses.TestAnnotation>")
));
}

Expand All @@ -54,8 +54,8 @@ void testWithDelegate()
new Has<>(TestAnnotation::defaultString, "def"))))),
new AllOf<>(
new Passes<Class<?>>(AnnotatedTestClass.class),
new Fails<>(TestClassWithoutAnnotation.class, "Class that had annotations [ ] did not contain { (0) instance of <interface org.saynotobugs.confidence.asm.quality.testclasses.TestAnnotation>\n (1) \"def\" }"),
new HasDescription("Class that has annotations contains { (0) instance of <interface org.saynotobugs.confidence.asm.quality.testclasses.TestAnnotation>\n (1) \"def\" }")
new Fails<>(TestClassWithoutAnnotation.class, "Class that had annotations [ ] did not contain (0) instance of <interface org.saynotobugs.confidence.asm.quality.testclasses.TestAnnotation>\n (1) \"def\""),
new HasDescription("Class that has annotations contains (0) instance of <interface org.saynotobugs.confidence.asm.quality.testclasses.TestAnnotation>\n (1) \"def\"")
));
}

Expand All @@ -67,8 +67,8 @@ void testToString()
new HasToString(FunctionalInterface.class.getCanonicalName()))))),
new AllOf<>(
new Passes<Class<?>>(FunctionalTestClass.class),
new Fails<>(TestClassWithoutAnnotation.class, "Class that had annotations [ ] did not contain { (0) instance of <interface java.lang.FunctionalInterface>\n (1) has toString() \"java.lang.FunctionalInterface\" }"),
new HasDescription("Class that has annotations contains { (0) instance of <interface java.lang.FunctionalInterface>\n (1) has toString() \"java.lang.FunctionalInterface\" }")
new Fails<>(TestClassWithoutAnnotation.class, "Class that had annotations [ ] did not contain (0) instance of <interface java.lang.FunctionalInterface>\n (1) has toString() \"java.lang.FunctionalInterface\""),
new HasDescription("Class that has annotations contains (0) instance of <interface java.lang.FunctionalInterface>\n (1) has toString() \"java.lang.FunctionalInterface\"")
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,88 +18,85 @@

package org.saynotobugs.confidence.quality.iterable;

import org.dmfs.jems2.iterable.Mapped;
import org.dmfs.jems2.iterable.Seq;
import org.dmfs.jems2.iterable.Sieved;
import org.dmfs.jems2.single.Collected;
import org.dmfs.jems2.single.Reduced;
import org.dmfs.jems2.optional.First;
import org.dmfs.srcless.annotations.staticfactory.StaticFactories;
import org.saynotobugs.confidence.Assessment;
import org.saynotobugs.confidence.Quality;
import org.saynotobugs.confidence.assessment.Fail;
import org.saynotobugs.confidence.assessment.Pass;
import org.saynotobugs.confidence.assessment.PassIf;
import org.saynotobugs.confidence.description.Spaced;
import org.saynotobugs.confidence.description.Structured;
import org.saynotobugs.confidence.description.Text;
import org.saynotobugs.confidence.description.Value;
import org.saynotobugs.confidence.quality.composite.QualityComposition;
import org.saynotobugs.confidence.quality.object.EqualTo;

import java.util.ArrayList;

import static org.saynotobugs.confidence.description.LiteralDescription.COMMA_NEW_LINE;


@StaticFactories(value = "Core", packageName = "org.saynotobugs.confidence.quality")
public final class Contains<T> extends QualityComposition<Iterable<T>>
{
/**
* Creates a {@link Quality} that, for each given values, checks if the {@link Iterable} under test contains at least one element that equals that value.
* A {@link Quality} that checks if the {@link Iterable} under test contains the given element.
* <p>
* Example
* <pre>
* assertThat(asList("foo", "bar", "baz"), contains("foo", "bar"));
* assertThat(asList("foo", "bar", "baz"), contains("foo"));
* </pre>
*/
@SafeVarargs
public Contains(T... values)
public Contains(T value)
{
this(new Mapped<>(EqualTo::new, new Seq<>(values)));
this(new EqualTo<>(value));
}


@SafeVarargs
public Contains(Quality<? super T>... delegates)
/**
* A {@link Quality} that checks if the {@link Iterable} under test contains an element that satisfies
* the given {@link Quality}.
* <p>
* Example
* <pre>
* assertThat(asList("foo", "bar", "baz"), contains(equalTo("foo")));
* </pre>
*/
public Contains(Quality<? super T> quality)
{
this(new Seq<>(delegates));
super(actual -> new PassIf(new First<>(element -> quality.assessmentOf(element).isSuccess(), actual).isPresent(),
new Spaced(new Value(actual), new Text("did not contain"), quality.description())),
new Spaced(new Text("contains"), quality.description()));
}


public Contains(Iterable<? extends Quality<? super T>> delegates)
/**
* Creates a {@link Quality} that, for each given values, checks if the {@link Iterable} under test contains at
* least one element that equals that value.
* <p>
* Example
* <pre>
* assertThat(asList("foo", "bar", "baz"), contains("foo", "bar"));
* </pre>
*
* @deprecated use {@link org.saynotobugs.confidence.quality.Core#containsAllOf(Object[])}
*/
@Deprecated
@SafeVarargs
public Contains(T... values)
{
super(actual -> assess(actual, delegates),
new Spaced(
new Text("contains {"),
new Structured(COMMA_NEW_LINE, new Mapped<>(Quality::description, delegates)),
new Text("}")));
super(new ContainsAllOf<>(values));
}


private static <T> Assessment assess(Iterable<T> actual, Iterable<? extends Quality<? super T>> delegates)
/**
* @deprecated use {@link org.saynotobugs.confidence.quality.Core#containsAllOf(Quality[])}
*/
@Deprecated
@SafeVarargs
public Contains(Quality<? super T>... delegates)
{
Iterable<? extends Quality<? super T>> missing = missing(actual, delegates);
if (missing.iterator().hasNext())
{
return new Fail(
new Spaced(
new Value(actual),
new Text("did not contain {"),
new Structured(COMMA_NEW_LINE, new Mapped<>(Quality::description, missing)),
new Text("}")));
}
return new Pass();
super(new ContainsAllOf<>(delegates));
}


private static <T> Iterable<? extends Quality<? super T>> missing(Iterable<T> actual, Iterable<? extends Quality<? super T>> delegates)
/**
* @deprecated use {@link org.saynotobugs.confidence.quality.Core#containsAllOf(Iterable)}
*/
@Deprecated
public Contains(Iterable<? extends Quality<? super T>> delegates)
{
return new Reduced<T, Iterable<? extends Quality<? super T>>>(
() -> delegates,
(missing, value) -> new Collected<>(
ArrayList::new,
new Sieved<>(delegate -> !delegate.assessmentOf(value).isSuccess(), missing)).value(),
actual)
.value();
super(new ContainsAllOf<>(delegates));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright 2022 dmfs GmbH
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.saynotobugs.confidence.quality.iterable;

import org.dmfs.jems2.iterable.Mapped;
import org.dmfs.jems2.iterable.Seq;
import org.dmfs.jems2.iterable.Sieved;
import org.dmfs.jems2.single.Collected;
import org.dmfs.jems2.single.Reduced;
import org.dmfs.srcless.annotations.staticfactory.StaticFactories;
import org.saynotobugs.confidence.Assessment;
import org.saynotobugs.confidence.Quality;
import org.saynotobugs.confidence.assessment.Fail;
import org.saynotobugs.confidence.assessment.Pass;
import org.saynotobugs.confidence.description.Spaced;
import org.saynotobugs.confidence.description.Structured;
import org.saynotobugs.confidence.description.Text;
import org.saynotobugs.confidence.description.Value;
import org.saynotobugs.confidence.quality.composite.QualityComposition;
import org.saynotobugs.confidence.quality.object.EqualTo;

import java.util.ArrayList;

import static org.saynotobugs.confidence.description.LiteralDescription.COMMA_NEW_LINE;


@StaticFactories(value = "Core", packageName = "org.saynotobugs.confidence.quality")
public final class ContainsAllOf<T> extends QualityComposition<Iterable<T>>
{
/**
* A {@link Quality} that, for each given value, checks if the {@link Iterable} under test contains at
* least one element that equals that value.
* <p>
* Example
* <pre>
* assertThat(asList("foo", "bar", "baz"), containsAllOf("foo", "bar"));
* </pre>
*/
@SafeVarargs
public ContainsAllOf(T... values)
{
this(new Mapped<>(EqualTo::new, new Seq<>(values)));
}


/**
* A {@link Quality} that, for each given {@link Quality}, checks if the {@link Iterable} under test
* contains at least one element that satisfies that {@link Quality}.
* <p>
* Example
* <pre>
* assertThat(asList("foo", "bazz", "foobar"), containsAllOf(equalTo("foo"), hasLength(3)));
* </pre>
*/
@SafeVarargs
public ContainsAllOf(Quality<? super T>... delegates)
{
this(new Seq<>(delegates));
}


public ContainsAllOf(Iterable<? extends Quality<? super T>> delegates)
{
super(actual -> assess(actual, delegates),
new Spaced(
new Text("contains all of {"),
new Structured(COMMA_NEW_LINE, new Mapped<>(Quality::description, delegates)),
new Text("}")));
}


private static <T> Assessment assess(Iterable<T> actual, Iterable<? extends Quality<? super T>> delegates)
{
Iterable<? extends Quality<? super T>> missing = missing(actual, delegates);
if (missing.iterator().hasNext())
{
return new Fail(
new Spaced(
new Value(actual),
new Text("did not contain {"),
new Structured(COMMA_NEW_LINE, new Mapped<>(Quality::description, missing)),
new Text("}")));
}
return new Pass();
}


private static <T> Iterable<? extends Quality<? super T>> missing(Iterable<T> actual, Iterable<? extends Quality<? super T>> delegates)
{
return new Reduced<T, Iterable<? extends Quality<? super T>>>(
() -> delegates,
(missing, value) -> new Collected<>(
ArrayList::new,
new Sieved<>(delegate -> !delegate.assessmentOf(value).isSuccess(), missing)).value(),
actual)
.value();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ void test()
assertThat(new ArrayThat(new Contains<>(3)),
new AllOf<>(
new Passes<Object>(new int[] { 1, 2, 3 }, new int[] { 3 }, new int[] { 3, 3, 3, 3, 3 }),
new Fails<Object>(new int[] {}, "(1) array [ ] did not contain { <3> }"),
new Fails<Object>(new int[] { 1, 2, 4 }, "(1) array [ <1>,\n <2>,\n <4> ] did not contain { <3> }"),
new Fails<Object>(new int[] {}, "(1) array [ ] did not contain <3>"),
new Fails<Object>(new int[] { 1, 2, 4 }, "(1) array [ <1>,\n <2>,\n <4> ] did not contain <3>"),
new Fails<>("abc", "(0) not an array"),
new HasDescription("(0) an array\n and\n (1) array that contains { <3> }")
new HasDescription("(0) an array\n and\n (1) array that contains <3>")
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ void testWithDescription()
new Spaced(new Text("adds <a> to list")), ArrayList::new, new SoIt<>(new Contains<>("a"))),
new AllOf<>(
new Passes<>(l -> l.add("a")),
new Fails<>(l -> l.add("b"), "Consumer that adds <a> to list but [ \"b\" ] did not contain { \"a\" }"),
new HasDescription("Consumer that adds <a> to list so it contains { \"a\" }")
new Fails<>(l -> l.add("b"), "Consumer that adds <a> to list but [ \"b\" ] did not contain \"a\""),
new HasDescription("Consumer that adds <a> to list so it contains \"a\"")
));
}

Expand All @@ -55,8 +55,8 @@ void testWithoutDescription()
assertThat(new ConsumerThatAffects<>(ArrayList::new, new SoIt<>(new Contains<>("a"))),
new AllOf<>(
new Passes<>(l -> l.add("a")),
new Fails<>(l -> l.add("b"), "Consumer that affects [ ] but [ \"b\" ] did not contain { \"a\" }"),
new HasDescription("Consumer that affects [ ] so it contains { \"a\" }")
new Fails<>(l -> l.add("b"), "Consumer that affects [ ] but [ \"b\" ] did not contain \"a\""),
new HasDescription("Consumer that affects [ ] so it contains \"a\"")
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ void test()
new When<>(new Maps<>("a", new To<>(true)))),
new AllOf<>(
new Passes<>(list -> list::add),
new Fails<>(list -> list::remove, "but [ ] did not contain { \"a\" } when mapped \"a\" to <false>"),
new HasDescription("mutates argument [ ] so it contains { \"a\" } when maps \"a\" to <true>")
new Fails<>(list -> list::remove, "but [ ] did not contain \"a\" when mapped \"a\" to <false>"),
new HasDescription("mutates argument [ ] so it contains \"a\" when maps \"a\" to <true>")
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ void test()
assertThat(new SoIt<>(new Contains<>("123")),
new AllOf<>(
new Passes<>(singletonList("123"), asList("1", "2", "123", "4")),
new Fails<>(asList("1", "2", "4"), "but [ \"1\",\n \"2\",\n \"4\" ] did not contain { \"123\" }"),
new HasDescription("so it contains { \"123\" }")
new Fails<>(asList("1", "2", "4"), "but [ \"1\",\n \"2\",\n \"4\" ] did not contain \"123\""),
new HasDescription("so it contains \"123\"")
));
}

Expand Down
Loading

0 comments on commit 7a24f7d

Please sign in to comment.