Skip to content

Commit

Permalink
new functionaljava-quickcheck module.
Browse files Browse the repository at this point in the history
  • Loading branch information
SimY4 committed Dec 6, 2024
1 parent b72ca14 commit 42b9d96
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 1 deletion.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ A handy utility for generating strings that match given regular expression crite
- [vavr-test](https://github.com/vavr-io/vavr-test)

# Usage
## Functionaljava Quickcheck
Include the following dependency into your project:

```groovy
testImplementation "com.github.simy4.coregex:coregex-functionaljava-quickcheck"
```

Use the provided `CoregexArbirary` class to generate a string that would match the regular expression predicate:

```java
@RunWith(PropertyTestRunner.class)
class MyTest {
public Property myProperty(@ForAll @Regex("[a-zA-Z]{3}") String str) {
return property(CoregexArbitrary.of("[a-zA-Z]{3}"), str -> prop(3 == str.length()));
}
}
```

## Jqwik
Include the following dependency into your project:

Expand Down
19 changes: 18 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ lazy val root = (project in file("."))
name := "coregex-parent",
publish / skip := true
)
.aggregate(core, jqwik, junitQuickcheck, kotest, scalacheck, vavrTest)
.aggregate(core, functionaljavaQuickcheck, jqwik, junitQuickcheck, kotest, scalacheck, vavrTest)

lazy val core = (project in file("core"))
.settings(
Expand All @@ -76,6 +76,23 @@ lazy val core = (project in file("core"))
.settings(javaLibSettings(8))
.settings(jacocoSettings)

lazy val functionaljavaQuickcheck = (project in file("functionaljava-quickcheck"))
.settings(
name := "functionaljava-quickcheck",
moduleName := "coregex-functionaljava-quickcheck",
description := "Functionaljava quickcheck bindings for coregex library.",
headerEndYear := Some(2024),
libraryDependencies ++= Seq(
"org.functionaljava" % "functionaljava-quickcheck" % "5.0" % Provided,
"junit" % "junit" % "4.13.2" % Test,
"com.github.sbt" % "junit-interface" % "0.13.3" % Test
),
testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-v")
)
.settings(javaLibSettings(8))
.settings(jacocoSettings)
.dependsOn(core)

lazy val jqwik = (project in file("jqwik"))
.settings(
name := "jqwik",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2021-2024 Alex Simkin
*
* 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 com.github.simy4.coregex.functionaljava.quickcheck;

import com.github.simy4.coregex.core.Coregex;
import com.github.simy4.coregex.core.rng.RandomRNG;
import fj.test.Gen;
import java.util.regex.Pattern;

public final class CoregexArbitrary {
public static Gen<String> of(String regex) {
return of(regex, 0);
}

public static Gen<String> of(String regex, int flags) {
Coregex coregex = Coregex.from(Pattern.compile(regex, flags));
return Gen.gen(
size -> {
Coregex sized = coregex.sized(Math.max(size, coregex.minLength()));
return rand -> sized.generate(new RandomRNG(rand.choose(Long.MIN_VALUE, Long.MAX_VALUE)));
});
}

private CoregexArbitrary() {
throw new UnsupportedOperationException("new");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2021-2024 Alex Simkin
*
* 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 com.github.simy4.coregex.functionaljava.quickcheck;

import static fj.test.Property.prop;
import static fj.test.Property.property;

import fj.Equal;
import fj.Try;
import fj.data.List;
import fj.test.Arbitrary;
import fj.test.Property;
import fj.test.runner.PropertyTestRunner;
import java.net.InetAddress;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import org.junit.runner.RunWith;

@RunWith(PropertyTestRunner.class)
public class CoregexArbitraryTest {
private static final Equal<String> stringEqual = Equal.stringEqual;
private static final Equal<List<String>> listEqual = Equal.listEqual(stringEqual);

public Property shouldGenerateMatchingUUIDString() {
return property(
CoregexArbitrary.of(
"[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}"),
uuid -> prop(stringEqual.eq(uuid, UUID.fromString(uuid).toString())));
}

public Property shouldGenerateMatchingIPv4String() {
return property(
CoregexArbitrary.of(
"((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])"),
ipv4 -> {
String[] expected = ipv4.split("\\.");
String[] actual =
Try.io(() -> InetAddress.getByName(ipv4).getHostAddress().split("\\."))
.safe()
.run()
.success();
return prop(expected.length == actual.length)
.and(
List.arrayList(expected)
.zip(List.arrayList(actual))
.foldLeft(
(acc, p) ->
acc.and(prop(Integer.parseInt(p._1()) == Integer.parseInt(p._2()))),
prop(true)));
});
}

public Property shouldGenerateMatchingIsoDateString() {
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
return property(
CoregexArbitrary.of(
"[12]\\d{3}-(?:0[1-9]|1[012])-(?:0[1-9]|1\\d|2[0-8])T(?:1\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d{2}[1-9])?Z"),
iso8601Date ->
prop(stringEqual.eq(iso8601Date, formatter.format(formatter.parse(iso8601Date)))));
}

public Property shouldGenerateUniqueStrings() {
return property(
Arbitrary.arbList(CoregexArbitrary.of("[a-zA-Z0-9]{32,}")),
strings ->
strings.foldLeft(
(acc, s) ->
acc.and(prop(s.length() >= 32))
.and(prop(s.chars().allMatch(Character::isLetterOrDigit))),
prop(true)));
}
}

0 comments on commit 42b9d96

Please sign in to comment.