Set
s¶
So far you've seen hardcoded Set
s via Set::of()
and integers one via Set::integers()
. But there are much more.
Tip
You can find more on Packagist.
Common methods¶
Map¶
For all Set
objects you can transform the generated values with a function of your own.
Let's say the code you want to prove needs a Password
object. You can wrap strings in your object like this:
use Innmind\BlackBox\Set;
$set = Set::strings()
->map(static fn(string $string) => new Password($string));
Now if you use $set
in a proof it will generate instances of Password
with a random string inside it.
Flat map¶
This method allows to generate a Set
configured from a randomly generated value from another Set
.
In most cases you'll want to use the Set::composite()
. This method will be useful for advanced cases.
One example is the ability to randomly generate Set
s with associated data. Let's say you want to randomly generate strings defining types (int
, string
, etc...) with an associated Set
to generate values of this type in order to build an imaginary Type
class:
use Innmind\BlackBox\{
Set,
Set\Seed,
};
$pairs = Set::of(
['int', Set::integers()],
['string', Set::strings()],
['float', Set::realNumbers()],
['bool', Set::of(true, false)],
// etc...
);
$types = $pairs->flatMap(static function(Seed $pair) {
[$type, $values] = $pair->unwrap();
return $values->map(static fn($value) => new Type($type, $value));
});
The $types
Set
could generate the values new Type('bool', true)
, new Type('bool', false)
, new Type('int', 42)
, etc...
This example is simple enough and could be expressed without the use of flatMap
. Like this:
use Innmind\BlackBox\Set;
$types = Set::either(
Set::integers()->map(static fn($value) => ['int', $value]),
Set::strings()->map(static fn($value) => ['string', $value]),
Set::realNumbers()->map(static fn($value) => ['float', $value]),
Set::of(true, false)->map(static fn($value) => ['bool', $value]),
)
->map(static function($pair) {
[$type, $value] = $pair;
return new Type($type, $value);
});
But say that now you want multiple values instead of a single one. With flatMap
it's straightforward, unlike with the other approach.
use Innmind\BlackBox\{
Set,
Set\Seed,
};
$pairs = Set::of(
['int', Set::integers()],
['string', Set::strings()],
['float', Set::realNumbers()],
['bool', Set::of(true, false)],
// etc...
);
$types = $pairs->flatMap(static function(Seed $pair) {
[$type, $values] = $pair->unwrap();
return Set::sequence($values)->map(
static fn($value) => new Type($type, $value),
);
});
use Innmind\BlackBox\Set;
$types = Set::either(
Set::sequence(Set::integers())->map(static fn($value) => ['int', $value]),
Set::sequence(Set::strings())->map(static fn($value) => ['string', $value]),
Set::sequence(Set::realNumbers())->map(static fn($value) => ['float', $value]),
Set::sequence(Set::of(true, false))->map(static fn($value) => ['bool', $value]),
)
->map(static function($pair) {
[$type, $value] = $pair;
return new Type($type, $value);
});
As you can see with flatMap
you can locally define what you want without having to change the Set
you rely on. Unlike the alternative where you need to change the initial Set
and thus impacting any other Set
that could depend on it.
Shrinking
In the examples above the value passed as argument to the flatMap
callable can't be shrunk. This is because we call ->unwrap()
to acces to the real value behind the Seed
object.
When the Seed
value is unwrapped, BlackBox can no longer track how it's used and thus can no longer shrink it.
In the examples above the seeded value can't be shrunk anyway because it comes from user provided values via Set::of()
.
A simple example to demonstrate the use case is prefixing a string
with an int
:
use Innmind\BlackBox\{
Set,
Set\Seed,
};
$set = Set::integers()->flatMap(
static fn(Seed $int) => Set::strings()->map(
static fn(string $string) => $int->map(
static fn(int $int) => $int.$string,
),
),
);
This way BlackBox knows every transformations of a seeded value and re-apply then after shrinking it.
And you can also compose multiple Seed
s via the Seed::flatMap()
method.
Randomness
By default the Set
returned by flatMap
will produce values with the same seed (the callable argument).
If you want a more wide range of seeded values you should call the ->randomize()
method after ->flatMap()
.
Filter¶
To reuse the password example from above. Say that your password needs to contain the character $
. You can do:
use Innmind\BlackBox\Set;
$set = Set::strings()
->filter(static fn(string $string) => \str_contains($string, '$'))
->map(static fn(string $string) => new Password($string));
Warning
This is an example. You should not enforce your passwords to have a specific value in it. The strength is based on length. (US and French recommendations)
Nullable¶
If you need a Set
to also generate a null
value you can simply do:
Primitives¶
Strings¶
This describes any chars that can be returned by the \chr()
function.
This describes the range a..z
.
This describes the range A..Z
.
This describes any character that you can typically find on your keyboard.
This describes any string of a length between 0
and 128
containing any character from Set::strings()->chars()
.
This is the same as Set::strings()
but you specify the length range.
This describes any single unicode character.
This is the same as Se::strings()->madeOf(Set::strings()->unicode()->char())
.
use Innmind\BlackBox\Set;
Set::strings()->unicode()->controlCharater();
Set::strings()->unicode()->basicLatin();
Set::strings()->unicode()->spacingModifierLetters();
// etc...
This set provides all the unicode blocks as dedicated methods.
Integers¶
This describes any integer between \PHP_INT_MIN
and \PHP_INT_MAX
.
This describes any integer between the bounds you specify.
This is the same as Set::integers()->above(0)
.
The bounds are included in the values that can be generated
Real numbers¶
This describes any float between \PHP_INT_MIN
and \PHP_INT_MAX
.
This describes any float between the bounds you specify.
The bounds are included in the values that can be generated
Type¶
This describes any type that is supported by PHP. This is useful to prove a code doesn't depend on the type of its arguments.
User defined values¶
Elements¶
This describes all the values that you put in (ie Set::of(true, false)
to describe booleans).
From a generator¶
This describes values that you will provide via a Generator
.
Higher order Set
s¶
Composite¶
This Set
allows to aggregate multiple values to a new one. Let's say you have a User
class, you could desribe it via:
Set::compose(
static fn(string $firstname, string $lastname) => new User(
$firstname,
$lastname,
),
Set::strings()->atLeast(1),
Set::strings()->atLeast(1),
);
Any additionnal Set
provided will give access to a new argument to the callable.
Either¶
You can think of this Set
as an OR.
This describes any integer or string.
Sequence¶
Set::sequence(Set)
describes a list (an array of consecutive values) of values of the given Set
type.
This describes any list of integers.
By default the list contains between 0
and 100
elements, you can change this via Set::sequence(Set)->between(min, max)
, ->atLeast()
or ->atMost()
.
The bounds are included.
Tuple¶
This is a special case of Composite
. Both examples does the same thing.
Call¶
This set is useful when building the Model to tests via properties. If BlackBox shrinks properties it will call the provided callable at each shrinking step. This allows to get rid of any state inside your Model between each run.
Specific types¶
Email¶
This describes any valid email string.