Viewing file: DatasetsRepository.php (7.04 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
declare(strict_types=1);
namespace Pest\Repositories;
use Closure; use Generator; use Pest\Exceptions\DatasetAlreadyExists; use Pest\Exceptions\DatasetDoesNotExist; use Pest\Exceptions\ShouldNotHappen; use Pest\Support\Exporter; use Traversable;
use function sprintf;
/** * @internal */ final class DatasetsRepository { private const SEPARATOR = '>>';
/** * Holds the datasets. * * @var array<string, Closure|iterable<int|string, mixed>> */ private static array $datasets = [];
/** * Holds the withs. * * @var array<array<string, Closure|iterable<int|string, mixed>|string>> */ private static array $withs = [];
/** * Sets the given. * * @param Closure|iterable<int|string, mixed> $data */ public static function set(string $name, Closure|iterable $data, string $scope): void { $datasetKey = "$scope".self::SEPARATOR."$name";
if (array_key_exists("$datasetKey", self::$datasets)) { throw new DatasetAlreadyExists($name, $scope); }
self::$datasets[$datasetKey] = $data; }
/** * Sets the given "with". * * @param array<Closure|iterable<int|string, mixed>|string> $with */ public static function with(string $filename, string $description, array $with): void { self::$withs["$filename".self::SEPARATOR."$description"] = $with; }
public static function has(string $filename, string $description): bool { return array_key_exists($filename.self::SEPARATOR.$description, self::$withs); }
/** * @return Closure|array<int|string, mixed> * * @throws ShouldNotHappen */ public static function get(string $filename, string $description): Closure|array { $dataset = self::$withs[$filename.self::SEPARATOR.$description];
$dataset = self::resolve($dataset, $filename);
if ($dataset === null) { throw ShouldNotHappen::fromMessage('Dataset [%s] not resolvable.'); }
return $dataset; }
/** * Resolves the current dataset to an array value. * * @param array<Closure|iterable<int|string, mixed>|string> $dataset * @return array<string, mixed>|null */ public static function resolve(array $dataset, string $currentTestFile): ?array { if ($dataset === []) { return null; }
$dataset = self::processDatasets($dataset, $currentTestFile);
$datasetCombinations = self::getDatasetsCombinations($dataset);
$datasetDescriptions = []; $datasetValues = [];
foreach ($datasetCombinations as $datasetCombination) { $partialDescriptions = []; $values = [];
foreach ($datasetCombination as $datasetCombinationElement) { $partialDescriptions[] = $datasetCombinationElement['label'];
// @phpstan-ignore-next-line $values = array_merge($values, $datasetCombinationElement['values']); }
$datasetDescriptions[] = implode(' / ', $partialDescriptions); $datasetValues[] = $values; }
foreach (array_count_values($datasetDescriptions) as $descriptionToCheck => $count) { if ($count > 1) { $index = 1; foreach ($datasetDescriptions as $i => $datasetDescription) { if ($datasetDescription === $descriptionToCheck) { $datasetDescriptions[$i] .= sprintf(' #%d', $index++); } } } }
$namedData = []; foreach ($datasetDescriptions as $i => $datasetDescription) { $namedData[$datasetDescription] = $datasetValues[$i]; }
return $namedData; }
/** * @param array<Closure|iterable<int|string, mixed>|string> $datasets * @return array<int, array<int, mixed>> */ private static function processDatasets(array $datasets, string $currentTestFile): array { $processedDatasets = [];
foreach ($datasets as $index => $data) { $processedDataset = [];
if (is_string($data)) { $datasets[$index] = self::getScopedDataset($data, $currentTestFile); }
if (is_callable($datasets[$index])) { $datasets[$index] = call_user_func($datasets[$index]); }
if ($datasets[$index] instanceof Traversable) { $preserveKeysForArrayIterator = $datasets[$index] instanceof Generator && is_string($datasets[$index]->key());
$datasets[$index] = iterator_to_array($datasets[$index], $preserveKeysForArrayIterator); }
foreach ($datasets[$index] as $key => $values) { $values = is_array($values) ? $values : [$values]; $processedDataset[] = [ 'label' => self::getDatasetDescription($key, $values), 'values' => $values, ]; }
$processedDatasets[] = $processedDataset; }
return $processedDatasets; }
/** * @return Closure|iterable<int|string, mixed> */ private static function getScopedDataset(string $name, string $currentTestFile): Closure|iterable { $matchingDatasets = array_filter(self::$datasets, function (string $key) use ($name, $currentTestFile): bool { [$datasetScope, $datasetName] = explode(self::SEPARATOR, $key);
if ($name !== $datasetName) { return false; }
return str_starts_with($currentTestFile, $datasetScope); }, ARRAY_FILTER_USE_KEY);
$closestScopeDatasetKey = array_reduce( array_keys($matchingDatasets), fn (string|int|null $keyA, string|int|null $keyB): string|int|null => $keyA !== null && strlen((string) $keyA) > strlen((string) $keyB) ? $keyA : $keyB );
if ($closestScopeDatasetKey === null) { throw new DatasetDoesNotExist($name); }
return $matchingDatasets[$closestScopeDatasetKey]; }
/** * @param array<array<mixed>> $combinations * @return array<array<array<mixed>>> */ private static function getDatasetsCombinations(array $combinations): array { $result = [[]]; foreach ($combinations as $index => $values) { $tmp = []; foreach ($result as $resultItem) { foreach ($values as $value) { $tmp[] = array_merge($resultItem, [$index => $value]); } } $result = $tmp; }
// @phpstan-ignore-next-line return $result; }
/** * @param array<int, mixed> $data */ private static function getDatasetDescription(int|string $key, array $data): string { $exporter = Exporter::default();
if (is_int($key)) { return sprintf('(%s)', $exporter->shortenedRecursiveExport($data)); }
return sprintf('dataset "%s"', $key); } }
|