Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/code_coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions

name: "Code Coverage"

on:
pull_request:
push:
branches:
- "main"

permissions:
contents: "read"

concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true

jobs:
code-coverage:
name: "Code Coverage"

runs-on: "ubuntu-latest"

steps:
- name: "Checkout"
uses: "actions/checkout@v7"

- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "pcov"
php-version: "8.5"

- name: "Install dependencies"
uses: "ramsey/composer-install@v4"

- name: "Tests with coverage"
run: "vendor/bin/phpunit --coverage-clover build/logs/clover.xml"

- name: "Upload to Codecov"
uses: "codecov/codecov-action@v7"
with:
token: "${{ secrets.CODECOV_TOKEN }}" # not required for public repos
files: "build/logs/clover.xml"
flags: "unittests"
fail_ci_if_error: true
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ vendor/bin/phpcs # coding standard (brainbits/phpcs-standa

CI (`.github/workflows/build.yml`) runs composer validate/audit, phpcs and phpstan on PHP 8.5 and the tests on
PHP 8.4 and 8.5 with lowest and highest dependencies. phpcs only runs with the highest dependencies; the lowest
coding standard version reports different violations.
coding standard version reports different violations. `.github/workflows/code_coverage.yaml` uploads coverage (pcov,
PHP 8.5) to Codecov. Line coverage is 100 %; `#[CoversFunction]` only counts lines of the named function, so internal
helpers need their own `Internal*Test`.

## How functions are built

Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# fnc

[![Coverage Status](https://img.shields.io/codecov/c/github/temp/fnc)](https://app.codecov.io/gh/temp/fnc)

A [Ramda](https://ramdajs.com/)-inspired functional programming library for PHP.

All functions live in the `Fnc` namespace, are plain functions (no classes) and most of them are
Expand Down Expand Up @@ -30,8 +32,8 @@ $adultNames([
composer require temp/fnc
```

The package was called `temp/fn` up to 1.0, so versions 1.0 and older are only available under that name. `temp/fnc`
replaces `temp/fn`, Composer never installs both. The namespace `Fnc` is unchanged.
The package was called `temp/fn` up to 1.0 and is abandoned in favour of `temp/fnc`, which also provides the older
versions. `temp/fnc` replaces `temp/fn`, Composer never installs both. The namespace `Fnc` is unchanged.

Requires PHP 8.4 or later. The functions are loaded via Composer's `files` autoloading, so importing them with
`use function Fnc\…;` is all you need.
Expand Down
1 change: 1 addition & 0 deletions tests/Unit/Curry5Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ public function testCurry3(): void
$this->assertEquals(17, $sum4(5, 10, -4)(4, 2));
$this->assertEquals(17, $sum4(5, 10, -4, 4, 2));
$this->assertEquals(17, $sum4(5, 10, -4, 4, 2, 8));
$this->assertEquals(17, $sum4(5, 10, -4, 4)(2));
}
}
46 changes: 46 additions & 0 deletions tests/Unit/InternalAcceptsArgsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace FncTests\Unit;

use PHPUnit\Framework\Attributes\CoversFunction;
use PHPUnit\Framework\TestCase;

use function Fnc\_acceptsArgs;
use function Fnc\curry2;

// phpcs:disable SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter

#[CoversFunction('Fnc\_acceptsArgs')]
final class InternalAcceptsArgsTest extends TestCase
{
public function testClosure(): void
{
$fn = static fn ($a, $b = null) => $a;

$this->assertTrue(_acceptsArgs($fn, 1));
$this->assertTrue(_acceptsArgs($fn, 2));
$this->assertFalse(_acceptsArgs($fn, 3));
}

public function testCurriedClosureUsesRegisteredArity(): void
{
$curried = curry2(static fn ($a, $b) => $a);

$this->assertTrue(_acceptsArgs($curried, 2));
$this->assertFalse(_acceptsArgs($curried, 3));
}

public function testVariadic(): void
{
$this->assertTrue(_acceptsArgs(static fn (...$args) => $args, 5));
}

public function testInternalFunctionCountsRequiredParametersOnly(): void
{
// trim() has an optional $characters parameter that must not receive a key
$this->assertTrue(_acceptsArgs('trim', 1));
$this->assertFalse(_acceptsArgs('trim', 2));
}
}
27 changes: 27 additions & 0 deletions tests/Unit/InternalReflectCallableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace FncTests\Unit;

use PHPUnit\Framework\Attributes\CoversFunction;
use PHPUnit\Framework\TestCase;
use RuntimeException;

use function Fnc\_reflectCallable;

#[CoversFunction('Fnc\_reflectCallable')]
final class InternalReflectCallableTest extends TestCase
{
public function testCallable(): void
{
$this->assertSame('trim', _reflectCallable('trim')->getName());
}

public function testNonCallableThrows(): void
{
$this->expectException(RuntimeException::class);

_reflectCallable('not a function');
}
}
8 changes: 8 additions & 0 deletions tests/Unit/SplitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace FncTests\Unit;

use InvalidArgumentException;
use PHPUnit\Framework\Attributes\CoversFunction;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -82,4 +83,11 @@ public function testWithMultipleMixedElements(): void

$this->assertSame(['foo', 'bar', '1', '2.5'], $result);
}

public function testEmptySeparatorThrows(): void
{
$this->expectException(InvalidArgumentException::class);

split('', 'foo');
}
}
Loading