* * Full license available in separate LICENSE file */ use Kadet\Functional as f; use Kadet\Functional\Collections as c; use Kadet\Functional\Predicates as p; use const Kadet\Functional\_; /** * The library's own callables (predicates, pipes, decorators) used as collection callbacks. */ class LibraryCallablesTest extends \PHPUnit\Framework\TestCase { // negated predicates public function testFilterWithNegatedInternalFunction() { $this->assertSame([1 => 'x'], c\filter(f\not(is_numeric(...)))(['1', 'x'])); } public function testFilterWithNegatedClosure() { $this->assertSame([1], c\filter(f\not(fn ($x) => $x > 1))([1, 2])); } public function testFilterWithNegatedPredicateMethod() { $this->assertSame([1], c\filter(p\gt(1)->not())([1, 2])); } public function testRejectWithNegatedPredicate() { $this->assertSame([1 => 2], c\reject(p\gt(1)->not())([1, 2])); } // pipelines get only the value when their first function takes only the value public function testMapWithPipelineOfInternalFunctions() { $slug = f\pipe(trim(...), strtolower(...), f\partial(str_replace(...), ' ', '-', _)); $this->assertSame(['n' => 'main-n'], c\map($slug)(['n' => 'Main n'])); } public function testMapWithPipelineOfInternalFunctionWithOptionalParameter() { $this->assertSame([1.0, 3.0, 3.0], c\map(f\pipe(round(...)))([1.25, 2.5, 3.14159])); } public function testMapWithPipelineOfMemoizedOneParameterClosure() { $calls = 0; $pipeline = f\pipe(f\memoize(function ($x) use (&$calls) { $calls++; return $x * 2; }), fn ($x) => $x + 1); $this->assertSame(['a' => 3, 'b' => 3], c\map($pipeline)(['a' => 1, 'b' => 1])); $this->assertSame(1, $calls); } public function testMapWithPipelineStartingWithTwoParameterClosureGetsKey() { $this->assertSame(['a' => 'A=X'], c\map(f\pipe(fn ($v, $k) => "$k=$v", strtoupper(...)))(['a' => 'x'])); } public function testFilterWithPredicateOfPipeline() { $a = (object) ['x' => 1]; $b = (object) ['x' => 2]; $this->assertSame([1 => $b], c\filter(p\property('x', p\gt(1)))([$a, $b])); } // predicate combinators forward to each predicate only what it takes public function testFilterWithAllOfInternalFunctionAndClosure() { $this->assertSame([0 => '1'], c\filter(f\all(is_numeric(...), fn ($x) => $x > 0))(['1', 'x', '-1'])); } public function testFilterWithAnyOfInternalFunctionAndClosure() { $this->assertSame([0 => '1', 2 => 'y'], c\filter(f\any(is_numeric(...), fn ($x) => $x === 'y'))(['1', 'x', 'y'])); } public function testFilterWithAllOfPassesKeyToPredicateTakingIt() { $this->assertSame(['b' => 2], c\filter(f\all(is_int(...), fn ($v, $k) => $k === 'b'))(['a' => 1, 'b' => 2])); } public function testFilterWithNegatedAllOfInternalFunctions() { $this->assertSame([1 => 'x'], c\filter(f\not(f\all(is_numeric(...), is_string(...))))(['1', 'x'])); } // decorators public function testMemoizedClosureIsCachedByValueOnly() { $calls = 0; $memoized = f\memoize(function ($x) use (&$calls) { $calls++; return $x * 2; }); $this->assertSame([2, 2, 2, 2], c\map($memoized)([1, 1, 1, 1])); $this->assertSame(1, $calls); } public function testMemoizedInternalFunctionGetsValueOnly() { $this->assertSame([1.0, 3.0, 3.0], c\map(f\memoize(round(...)))([1.25, 2.5, 3.14159])); } }