From 8462e7d31f782a079603f2659203afa4b3da6a8e Mon Sep 17 00:00:00 2001 From: Marten Gajda Date: Sun, 31 Dec 2023 00:04:46 +0100 Subject: [PATCH] containsNoneOf Quality, closes #161 --- .../quality/iterable/ContainsNoneOf.java | 116 ++++++++++++++++++ .../quality/iterable/ContainsNoneOfTest.java | 93 ++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 confidence-core/src/main/java/org/saynotobugs/confidence/quality/iterable/ContainsNoneOf.java create mode 100644 confidence-core/src/test/java/org/saynotobugs/confidence/quality/iterable/ContainsNoneOfTest.java diff --git a/confidence-core/src/main/java/org/saynotobugs/confidence/quality/iterable/ContainsNoneOf.java b/confidence-core/src/main/java/org/saynotobugs/confidence/quality/iterable/ContainsNoneOf.java new file mode 100644 index 00000000..85895f95 --- /dev/null +++ b/confidence-core/src/main/java/org/saynotobugs/confidence/quality/iterable/ContainsNoneOf.java @@ -0,0 +1,116 @@ +/* + * 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.BiFunction; +import org.dmfs.jems2.iterable.Mapped; +import org.dmfs.jems2.iterable.Numbered; +import org.dmfs.jems2.iterable.Seq; +import org.dmfs.jems2.iterable.Sieved; +import org.dmfs.jems2.predicate.Not; +import org.dmfs.srcless.annotations.staticfactory.StaticFactories; +import org.saynotobugs.confidence.Assessment; +import org.saynotobugs.confidence.Description; +import org.saynotobugs.confidence.Quality; +import org.saynotobugs.confidence.assessment.AllPassed; +import org.saynotobugs.confidence.assessment.Fail; +import org.saynotobugs.confidence.assessment.FailUpdated; +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 static org.saynotobugs.confidence.description.LiteralDescription.COMMA_NEW_LINE; + + +@StaticFactories(value = "Core", packageName = "org.saynotobugs.confidence.quality") +public final class ContainsNoneOf extends QualityComposition> +{ + /** + * A {@link Quality} that, for each given value, checks if the {@link Iterable} under test contains at + * least one element that equals that value. + *

+ * Example + *

+     * assertThat(asList("foo", "bar", "baz"), containsNoneOf("bazz", "foobar"));
+     * 
+ */ + @SafeVarargs + public ContainsNoneOf(T... values) + { + this(new Mapped<>(EqualTo::new, new Seq<>(values)), (valueDescription, missmatchesDescription) -> valueDescription); + } + + + /** + * 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}. + *

+ * Example + *

+     * assertThat(asList("foo", "bar", "foobar"), containsNoneOf(equalTo("bazz"), hasLength(5)));
+     * 
+ */ + @SafeVarargs + public ContainsNoneOf(Quality... delegates) + { + this(new Seq<>(delegates), Spaced::new); + } + + + public ContainsNoneOf(Iterable> delegates) + { + this(delegates, Spaced::new); + } + + + private ContainsNoneOf(Iterable> delegates, + BiFunction descriptionFunction) + { + super(actual -> new AllPassed(new Text("contained { "), COMMA_NEW_LINE, new Text(" }"), assess(actual, delegates, descriptionFunction)), + new Spaced( + new Text("contains none of {"), + new Structured(COMMA_NEW_LINE, new Mapped<>(Quality::description, delegates)), + new Text("}"))); + } + + private static Iterable assess( + Iterable actual, + Iterable> delegates, + BiFunction descriptionFunction) + { + return new Mapped<>( + numberedElement -> new FailUpdated( + description -> descriptionFunction.value(new Spaced(new Text(numberedElement.left() + ":"), new Value(numberedElement.right())), description), + new AllPassed( + new Text("{ "), COMMA_NEW_LINE, new Text(" }"), + new Sieved<>(new Not<>(Assessment::isSuccess), + new Mapped<>( + delegate -> delegate.assessmentOf(numberedElement.right()).isSuccess() + ? new Fail(delegate.description()) + : new Pass(), + delegates)))), + new Numbered<>(actual) + ); + } + +} diff --git a/confidence-core/src/test/java/org/saynotobugs/confidence/quality/iterable/ContainsNoneOfTest.java b/confidence-core/src/test/java/org/saynotobugs/confidence/quality/iterable/ContainsNoneOfTest.java new file mode 100644 index 00000000..9356e909 --- /dev/null +++ b/confidence-core/src/test/java/org/saynotobugs/confidence/quality/iterable/ContainsNoneOfTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2023 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.Seq; +import org.junit.jupiter.api.Test; +import org.saynotobugs.confidence.quality.charsequence.MatchesPattern; +import org.saynotobugs.confidence.quality.composite.AllOf; +import org.saynotobugs.confidence.quality.object.EqualTo; +import org.saynotobugs.confidence.test.quality.Fails; +import org.saynotobugs.confidence.test.quality.HasDescription; +import org.saynotobugs.confidence.test.quality.Passes; + +import static org.saynotobugs.confidence.Assertion.assertThat; + +class ContainsNoneOfTest +{ + @Test + void testLiteral() + { + assertThat(new ContainsNoneOf<>("a", "b", "c"), + new AllOf<>( + new Passes<>(new Seq<>(), new Seq<>("1", "2", "3")), + new Fails<>(new Seq<>("1", "a", "3"), + "contained { ...\n 1: \"a\"\n ... }"), + new Fails<>(new Seq<>("1", "a", "c", "5"), + "contained { ...\n 1: \"a\",\n 2: \"c\"\n ... }"), + new Fails<>(new Seq<>("c", "b", "a"), + "contained { 0: \"c\",\n 1: \"b\",\n 2: \"a\" }"), + new Fails<>(new Seq<>("a", "a", "a"), + "contained { 0: \"a\",\n 1: \"a\",\n 2: \"a\" }"), + new Fails<>(new Seq<>("a", "a", "a", "b", "b", "b"), + "contained { 0: \"a\",\n 1: \"a\",\n 2: \"a\",\n 3: \"b\",\n 4: \"b\",\n 5: \"b\" }"), + new HasDescription("contains none of { \"a\",\n \"b\",\n \"c\" }") + )); + } + + @Test + void testQuality() + { + assertThat(new ContainsNoneOf<>(new MatchesPattern("[abc]"), new EqualTo<>("b")), + new AllOf<>( + new Passes<>(new Seq<>(), new Seq<>("1", "2", "3")), + new Fails<>(new Seq<>("1", "a", "3"), + "contained { ...\n 1: \"a\" { matches pattern /[abc]/ }\n ... }"), + new Fails<>(new Seq<>("1", "a", "c", "5"), + "contained { ...\n 1: \"a\" { matches pattern /[abc]/ },\n 2: \"c\" { matches pattern /[abc]/ }\n ... }"), + new Fails<>(new Seq<>("c", "b", "a"), + "contained { 0: \"c\" { matches pattern /[abc]/ },\n 1: \"b\" { matches pattern /[abc]/,\n \"b\" },\n 2: \"a\" { matches pattern /[abc]/ } }"), + new Fails<>(new Seq<>("a", "a", "a"), + "contained { 0: \"a\" { matches pattern /[abc]/ },\n 1: \"a\" { matches pattern /[abc]/ },\n 2: \"a\" { matches pattern /[abc]/ } }"), + new Fails<>(new Seq<>("a", "a", "a", "b", "b", "b"), + "contained { 0: \"a\" { matches pattern /[abc]/ },\n 1: \"a\" { matches pattern /[abc]/ },\n 2: \"a\" { matches pattern /[abc]/ },\n 3: \"b\" { matches pattern /[abc]/,\n \"b\" },\n 4: \"b\" { matches pattern /[abc]/,\n \"b\" },\n 5: \"b\" { matches pattern /[abc]/,\n \"b\" } }"), + new HasDescription("contains none of { matches pattern /[abc]/,\n \"b\" }") + )); + } + + @Test + void testQualityIterable() + { + assertThat(new ContainsNoneOf<>(new Seq<>(new MatchesPattern("[abc]"), new EqualTo<>("b"))), + new AllOf<>( + new Passes<>(new Seq<>(), new Seq<>("1", "2", "3")), + new Fails<>(new Seq<>("1", "a", "3"), + "contained { ...\n 1: \"a\" { matches pattern /[abc]/ }\n ... }"), + new Fails<>(new Seq<>("1", "a", "c", "5"), + "contained { ...\n 1: \"a\" { matches pattern /[abc]/ },\n 2: \"c\" { matches pattern /[abc]/ }\n ... }"), + new Fails<>(new Seq<>("c", "b", "a"), + "contained { 0: \"c\" { matches pattern /[abc]/ },\n 1: \"b\" { matches pattern /[abc]/,\n \"b\" },\n 2: \"a\" { matches pattern /[abc]/ } }"), + new Fails<>(new Seq<>("a", "a", "a"), + "contained { 0: \"a\" { matches pattern /[abc]/ },\n 1: \"a\" { matches pattern /[abc]/ },\n 2: \"a\" { matches pattern /[abc]/ } }"), + new Fails<>(new Seq<>("a", "a", "a", "b", "b", "b"), + "contained { 0: \"a\" { matches pattern /[abc]/ },\n 1: \"a\" { matches pattern /[abc]/ },\n 2: \"a\" { matches pattern /[abc]/ },\n 3: \"b\" { matches pattern /[abc]/,\n \"b\" },\n 4: \"b\" { matches pattern /[abc]/,\n \"b\" },\n 5: \"b\" { matches pattern /[abc]/,\n \"b\" } }"), + new HasDescription("contains none of { matches pattern /[abc]/,\n \"b\" }") + )); + } +} \ No newline at end of file