From 1d6735911c2a54a81f36c3eee1e38ebf8cf037f9 Mon Sep 17 00:00:00 2001 From: Simon Bigelmayr Date: Wed, 8 May 2024 12:39:31 +0200 Subject: [PATCH] Illumina sample sheet v2 (#19) * feature: Add Support Illumina NovaSeq Sample Sheets --- CHANGELOG.md | 6 ++ Makefile | 2 +- composer.json | 2 +- rector.php | 4 + .../IlluminaSampleSheetException.php | 5 + src/IlluminaSampleSheet/SampleSheet.php | 24 +++++ src/IlluminaSampleSheet/Section.php | 8 ++ .../V2/BclConvert/BclConvertSection.php | 28 ++++++ .../V2/BclConvert/BclSample.php | 51 +++++++++++ .../V2/BclConvert/DataSection.php | 52 +++++++++++ .../V2/BclConvert/SettingsSection.php | 42 +++++++++ .../V2/Enums/FastQCompressionFormat.php | 26 ++++++ src/IlluminaSampleSheet/V2/HeaderSection.php | 56 ++++++++++++ .../V2/NovaSeqXSampleSheet.php | 21 +++++ src/IlluminaSampleSheet/V2/ReadsSection.php | 55 +++++++++++ tests/BavarianHolidaysTest.php | 3 + tests/CSVArrayTest.php | 1 + tests/IlluminaSampleSheet/SampleSheetTest.php | 25 +++++ .../V2/NovaSeqXCloudSampleSheetTest.php | 91 +++++++++++++++++++ tests/Microplate/CoordinateSystemTest.php | 1 + tests/Microplate/CoordinatesTest.php | 9 ++ .../MicroplateSet/MicroplateSetABCDTest.php | 2 + .../MicroplateSet/MicroplateSetABTest.php | 2 + tests/NumberTest.php | 1 + tests/StringUtilTest.php | 3 + 25 files changed, 518 insertions(+), 2 deletions(-) create mode 100644 src/IlluminaSampleSheet/IlluminaSampleSheetException.php create mode 100644 src/IlluminaSampleSheet/SampleSheet.php create mode 100644 src/IlluminaSampleSheet/Section.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/BclConvertSection.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/BclSample.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/DataSection.php create mode 100644 src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php create mode 100644 src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php create mode 100644 src/IlluminaSampleSheet/V2/HeaderSection.php create mode 100644 src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php create mode 100644 src/IlluminaSampleSheet/V2/ReadsSection.php create mode 100644 tests/IlluminaSampleSheet/SampleSheetTest.php create mode 100644 tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 526f1ca..b1a8836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ See [GitHub releases](https://github.com/mll-lab/php-utils/releases). ## Unreleased +## v1.14.0 + +### Added + +- Support creating Illumina NovaSeq Sample Sheets (V2) for NovaSeqX + ## v1.13.0 ### Added diff --git a/Makefile b/Makefile index 50f420f..a25e4ec 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ stan: vendor ## Runs a static analysis with phpstan .PHONY: test test: vendor ## Runs auto-review, unit, and integration tests with phpunit mkdir --parents .build/phpunit - vendor/bin/phpunit --cache-result-file=.build/phpunit/result.cache + vendor/bin/phpunit --cache-directory=.build/phpunit vendor: composer.json composer validate --strict diff --git a/composer.json b/composer.json index 900532e..5c18be1 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "phpstan/phpstan-phpunit": "^1", "phpstan/phpstan-strict-rules": "^1", "phpunit/phpunit": "^9 || ^10 || ^11", - "rector/rector": "^0.17", + "rector/rector": "^1", "thecodingmachine/phpstan-safe-rule": "^1.2" }, "suggest": { diff --git a/rector.php b/rector.php index 9e93ade..5444e6d 100644 --- a/rector.php +++ b/rector.php @@ -2,6 +2,7 @@ use Rector\CodeQuality\Rector\Concat\JoinStringConcatRector; use Rector\Config\RectorConfig; +use Rector\PHPUnit\Rector\Class_\PreferPHPUnitSelfCallRector; use Rector\Set\ValueObject\SetList; return static function (RectorConfig $rectorConfig): void { @@ -17,6 +18,9 @@ __DIR__ . '/tests/CSVArrayTest.php', // keep `\r\n` for readability ], ]); + + $rectorConfig->rule(PreferPHPUnitSelfCallRector::class); + $rectorConfig->paths([__DIR__ . '/src', __DIR__ . '/tests']); $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon'); }; diff --git a/src/IlluminaSampleSheet/IlluminaSampleSheetException.php b/src/IlluminaSampleSheet/IlluminaSampleSheetException.php new file mode 100644 index 0000000..37a71c5 --- /dev/null +++ b/src/IlluminaSampleSheet/IlluminaSampleSheetException.php @@ -0,0 +1,5 @@ + */ + protected array $sections = []; + + public function addSection(Section $section): void + { + $this->sections[] = $section; + } + + public function toString(): string + { + $sectionStrings = array_map( + fn (Section $section): string => $section->convertSectionToString(), + $this->sections + ); + + return implode("\n", $sectionStrings); + } +} diff --git a/src/IlluminaSampleSheet/Section.php b/src/IlluminaSampleSheet/Section.php new file mode 100644 index 0000000..c571129 --- /dev/null +++ b/src/IlluminaSampleSheet/Section.php @@ -0,0 +1,8 @@ +settingsSection = $settingsSection; + $this->dataSection = $dataSection; + } + + public function convertSectionToString(): string + { + $bclConvertLines = [ + $this->settingsSection->convertSectionToString(), + $this->dataSection->convertSectionToString(), + ]; + + return implode("\n", $bclConvertLines); + } +} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php new file mode 100644 index 0000000..23c9bf4 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvert/BclSample.php @@ -0,0 +1,51 @@ +lane = $lane; + $this->sample_ID = $sample_ID; + $this->index = $index; + } + + /** @return array */ + public function toArray(): array + { + return array_filter([ + $this->lane, + $this->sample_ID, + $this->index, + $this->index2, + $this->overrideCycles, + $this->adapterRead1, + $this->adapterRead2, + $this->barcodeMismatchesIndex1, + $this->barcodeMismatchesIndex2, + ]); + } +} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php b/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php new file mode 100644 index 0000000..e26cc39 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvert/DataSection.php @@ -0,0 +1,52 @@ + */ + protected array $dataRows = []; + + public function addSample(BclSample $bclSample): void + { + $this->dataRows[] = $bclSample; + } + + public function convertSectionToString(): string + { + /** @var array $samplePropertiesOfFirstSample */ + $samplePropertiesOfFirstSample = array_keys(get_object_vars($this->dataRows[0])); + foreach ($this->dataRows as $sample) { + $actualProperties = array_keys(get_object_vars($sample)); + if ($samplePropertiesOfFirstSample !== $actualProperties) { + throw new \Exception('All samples must have the same properties. Expected: ' . \Safe\json_encode($samplePropertiesOfFirstSample) . ', Actual: ' . \Safe\json_encode($actualProperties)); + } + } + + $bclConvertDataHeaderLines = $this->generateDataHeaderByProperties($samplePropertiesOfFirstSample); + + $bclConvertDataLines = [ + '[BCLConvert_Data]', + $bclConvertDataHeaderLines, + ]; + + foreach ($this->dataRows as $dataRow) { + $bclConvertDataLines[] = implode(',', $dataRow->toArray()); + } + + return implode("\n", $bclConvertDataLines) . "\n"; + } + + /** @param array $samplePropertiesOfFirstSample */ + protected function generateDataHeaderByProperties(array $samplePropertiesOfFirstSample): string + { + $samplePropertiesOfFirstSample = array_filter($samplePropertiesOfFirstSample, fn (string $value) // @phpstan-ignore-next-line Variable property access on a non-object required here + => $this->dataRows[0]->$value !== null); + + $samplePropertiesOfFirstSample = array_map(fn (string $value) => ucfirst($value), $samplePropertiesOfFirstSample); + + return implode(',', $samplePropertiesOfFirstSample); + } +} diff --git a/src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php b/src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php new file mode 100644 index 0000000..7279c24 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/BclConvert/SettingsSection.php @@ -0,0 +1,42 @@ +softwareVersion = $softwareVersion; + $this->fastqCompressionFormat = $fastqCompressionFormat; + } + + public function convertSectionToString(): string + { + $bclConvertSettingsLines = [ + '[BCLConvert_Settings]', + "SoftwareVersion,{$this->softwareVersion}", + "FastqCompressionFormat,{$this->fastqCompressionFormat->value}", + ]; + + if (! is_null($this->trimUMI)) { + $trimUMIAsString = $this->trimUMI + ? '1' + : '0'; + + $bclConvertSettingsLines[] = "TrimUMI,{$trimUMIAsString}"; + } + + return implode("\n", $bclConvertSettingsLines) . "\n"; + } +} diff --git a/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php b/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php new file mode 100644 index 0000000..cceb713 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/Enums/FastQCompressionFormat.php @@ -0,0 +1,26 @@ +value = $value; + } + + public static function GZIP(): self + { + return new self(self::GZIP); + } + + public static function DRAGEN(): self + { + return new self(self::DRAGEN); + } +} diff --git a/src/IlluminaSampleSheet/V2/HeaderSection.php b/src/IlluminaSampleSheet/V2/HeaderSection.php new file mode 100644 index 0000000..04089f2 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/HeaderSection.php @@ -0,0 +1,56 @@ + */ + protected array $customParams = []; + + public function __construct(string $runName) + { + $this->runName = $runName; + } + + public function setCustomParam(string $paramName, string $paramValue): void + { + $this->customParams['Custom_' . $paramName] = $paramValue; + } + + public function convertSectionToString(): string + { + $headerLines = [ + '[Header]', + "FileFormatVersion,{$this->fileFormatVersion}", + "RunName,{$this->runName}", + ]; + if (! is_null($this->runDescription)) { + $headerLines[] = "RunDescription,{$this->runDescription}"; + } + if (! is_null($this->instrumentType)) { + $headerLines[] = "InstrumentType,{$this->instrumentType}"; + } + if (! is_null($this->instrumentPlatform)) { + $headerLines[] = "InstrumentPlatform,{$this->instrumentPlatform}"; + } + foreach ($this->customParams as $paramName => $paramValue) { + $headerLines[] = "{$paramName},{$paramValue}"; + } + + return implode("\n", $headerLines) . "\n"; + } +} diff --git a/src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php b/src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php new file mode 100644 index 0000000..e1858dc --- /dev/null +++ b/src/IlluminaSampleSheet/V2/NovaSeqXSampleSheet.php @@ -0,0 +1,21 @@ +addSection($header); + $this->addSection($reads); + if (! is_null($bclConvertSection)) { + $this->addSection($bclConvertSection); + } + } +} diff --git a/src/IlluminaSampleSheet/V2/ReadsSection.php b/src/IlluminaSampleSheet/V2/ReadsSection.php new file mode 100644 index 0000000..590fde1 --- /dev/null +++ b/src/IlluminaSampleSheet/V2/ReadsSection.php @@ -0,0 +1,55 @@ + 12)) { + throw new IlluminaSampleSheetException('Index1Cycles must be between 6 and 12 or null.'); + } + if ($index2Cycles !== null && ($index2Cycles < 6 || $index2Cycles > 12)) { + throw new IlluminaSampleSheetException('Index2Cycles must be between 6 and 12 or null.'); + } + $this->read1Cycles = $read1Cycles; + $this->read2Cycles = $read2Cycles; + $this->index1Cycles = $index1Cycles; + $this->index2Cycles = $index2Cycles; + } + + public function convertSectionToString(): string + { + $readsLines = ['[Reads]']; + $readsLines[] = "Read1Cycles,{$this->read1Cycles}"; + + if ($this->read2Cycles !== null) { + $readsLines[] = "Read2Cycles,{$this->read2Cycles}"; + } + if ($this->index1Cycles !== null) { + $readsLines[] = "Index1Cycles,{$this->index1Cycles}"; + } + if ($this->index2Cycles !== null) { + $readsLines[] = "Index2Cycles,{$this->index2Cycles}"; + } + + return implode("\n", $readsLines) . "\n"; + } +} diff --git a/tests/BavarianHolidaysTest.php b/tests/BavarianHolidaysTest.php index 77d55a4..aad68a2 100644 --- a/tests/BavarianHolidaysTest.php +++ b/tests/BavarianHolidaysTest.php @@ -15,6 +15,7 @@ public function testNameHoliday(): void self::assertSame(BavarianHolidays::OSTERSONNTAG, BavarianHolidays::nameHoliday(self::easterSunday2019())); } + #[\PHPUnit\Framework\Attributes\DataProvider('businessDays')] /** @dataProvider businessDays */ public function testBusinessDays(Carbon $businessDay): void { @@ -22,6 +23,7 @@ public function testBusinessDays(Carbon $businessDay): void self::assertFalse(BavarianHolidays::isHoliday($businessDay)); } + #[\PHPUnit\Framework\Attributes\DataProvider('holidays')] /** @dataProvider holidays */ public function testHolidays(Carbon $holiday): void { @@ -29,6 +31,7 @@ public function testHolidays(Carbon $holiday): void self::assertTrue(BavarianHolidays::isHoliday($holiday)); } + #[\PHPUnit\Framework\Attributes\DataProvider('weekend')] /** @dataProvider weekend */ public function testWeekend(Carbon $weekend): void { diff --git a/tests/CSVArrayTest.php b/tests/CSVArrayTest.php index 6ce0500..991593c 100644 --- a/tests/CSVArrayTest.php +++ b/tests/CSVArrayTest.php @@ -46,6 +46,7 @@ public static function csvAndArrayStringValues(): iterable * * @param array> $array */ + #[\PHPUnit\Framework\Attributes\DataProvider('csvAndArrayStringValues')] public function testStringValues(string $csv, array $array): void { self::assertSame($array, CSVArray::toArray($csv)); diff --git a/tests/IlluminaSampleSheet/SampleSheetTest.php b/tests/IlluminaSampleSheet/SampleSheetTest.php new file mode 100644 index 0000000..6f51dde --- /dev/null +++ b/tests/IlluminaSampleSheet/SampleSheetTest.php @@ -0,0 +1,25 @@ +createMock(Section::class); + $sectionMock1->method('convertSectionToString')->willReturn('section1'); + + $sectionMock2 = $this->createMock(Section::class); + $sectionMock2->method('convertSectionToString')->willReturn('section2'); + + $sampleSheet = $this->createPartialMock(SampleSheet::class, []); + $sampleSheet->addSection($sectionMock1); + $sampleSheet->addSection($sectionMock2); + + self::assertSame("section1\nsection2", $sampleSheet->toString()); + } +} diff --git a/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php b/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php new file mode 100644 index 0000000..af808be --- /dev/null +++ b/tests/IlluminaSampleSheet/V2/NovaSeqXCloudSampleSheetTest.php @@ -0,0 +1,91 @@ +instrumentPlatform = 'NovaSeqXSeries'; + $headerSection->setCustomParam('IndexOrientation', 'Orientation1'); + + $readsSection = new ReadsSection( + 100, + 101, + 10, + 11 + ); + + $bclConvertSettingsSection = new SettingsSection('1.0.0', FastQCompressionFormat::GZIP()); + $bclConvertSettingsSection->trimUMI = false; + + $bclConvertDataSection = new DataSection(); + + $bclSample = new BclSample(1, 'Sample1', 'Index1'); + $bclSample->index2 = 'Index2'; + $bclSample->overrideCycles = 'Cycles1'; + $bclSample->adapterRead1 = 'Adapter1'; + $bclSample->adapterRead2 = 'Adapter2'; + + $bclSample1 = new BclSample(2, 'Sample2', 'Index3'); + $bclSample1->index2 = 'Index4'; + $bclSample1->overrideCycles = 'Cycles2'; + $bclSample1->adapterRead1 = 'Adapter3'; + $bclSample1->adapterRead2 = 'Adapter4'; + + $bclSample2 = new BclSample(3, 'Sample3', 'Index5'); + $bclSample2->index2 = 'Index6'; + $bclSample2->overrideCycles = 'Cycles3'; + $bclSample2->adapterRead1 = 'Adapter5'; + $bclSample2->adapterRead2 = 'Adapter6'; + + $bclConvertDataSection->addSample($bclSample); + $bclConvertDataSection->addSample($bclSample1); + $bclConvertDataSection->addSample($bclSample2); + + $bclConvertSection = new BclConvertSection($bclConvertSettingsSection, $bclConvertDataSection); + + $novaSeqXCloudSampleSheet = new NovaSeqXSampleSheet( + $headerSection, + $readsSection, + $bclConvertSection, + ); + + $expected = '[Header] +FileFormatVersion,2 +RunName,Run1 +InstrumentPlatform,NovaSeqXSeries +Custom_IndexOrientation,Orientation1 + +[Reads] +Read1Cycles,100 +Read2Cycles,101 +Index1Cycles,10 +Index2Cycles,11 + +[BCLConvert_Settings] +SoftwareVersion,1.0.0 +FastqCompressionFormat,gzip +TrimUMI,0 + +[BCLConvert_Data] +Lane,Sample_ID,Index,Index2,OverrideCycles,AdapterRead1,AdapterRead2 +1,Sample1,Index1,Index2,Cycles1,Adapter1,Adapter2 +2,Sample2,Index3,Index4,Cycles2,Adapter3,Adapter4 +3,Sample3,Index5,Index6,Cycles3,Adapter5,Adapter6 +'; + + self::assertSame($expected, $novaSeqXCloudSampleSheet->toString()); + } +} diff --git a/tests/Microplate/CoordinateSystemTest.php b/tests/Microplate/CoordinateSystemTest.php index 923b61a..287a501 100644 --- a/tests/Microplate/CoordinateSystemTest.php +++ b/tests/Microplate/CoordinateSystemTest.php @@ -10,6 +10,7 @@ final class CoordinateSystemTest extends TestCase { + #[\PHPUnit\Framework\Attributes\DataProvider('firstLast')] /** @dataProvider firstLast */ public function testFirstLast(CoordinateSystem $coordinateSystem, string $expectedFirst, string $expectedLast): void { diff --git a/tests/Microplate/CoordinatesTest.php b/tests/Microplate/CoordinatesTest.php index 8609c70..ee902a9 100644 --- a/tests/Microplate/CoordinatesTest.php +++ b/tests/Microplate/CoordinatesTest.php @@ -10,6 +10,7 @@ final class CoordinatesTest extends TestCase { + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider96Well')] /** @dataProvider dataProvider96Well */ public function testCanConstructFromRowAndColumn(string $row, int $column, int $rowFlowPosition, int $columnFlowPosition): void { @@ -18,6 +19,7 @@ public function testCanConstructFromRowAndColumn(string $row, int $column, int $ self::assertSame($row . $column, $coordinates96Well->toString()); } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider96Well')] /** @dataProvider dataProvider96Well */ public function testCanConstructFromPosition(string $row, int $column, int $rowFlowPosition, int $columnFlowPosition): void { @@ -40,6 +42,7 @@ public function testCanConstructFromPosition(string $row, int $column, int $rowF self::assertSame($column, $coordinates->column); } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider96Well')] /** @dataProvider dataProvider96Well */ public function testFromCoordinatesString(string $row, int $column, int $rowFlowPosition, int $columnFlowPosition): void { @@ -48,6 +51,7 @@ public function testFromCoordinatesString(string $row, int $column, int $rowFlow self::assertSame($column, $coordinates->column); } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderPadded96Well')] /** @dataProvider dataProviderPadded96Well */ public function testFromPaddedCoordinatesString(string $paddedCoordinates, string $row, int $column): void { @@ -88,6 +92,7 @@ public static function dataProviderPadded96Well(): iterable ]; } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider96Well')] /** @dataProvider dataProvider96Well */ public function testPosition96Well(string $row, int $column, int $rowFlowPosition, int $columnFlowPosition): void { @@ -684,6 +689,7 @@ public static function dataProvider96Well(): iterable ]; } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider12Well')] /** @dataProvider dataProvider12Well */ public function testPosition12Well(string $row, int $column, int $rowFlowPosition, int $columnFlowPosition): void { @@ -771,6 +777,7 @@ public static function dataProvider12Well(): array ]; } + #[\PHPUnit\Framework\Attributes\DataProvider('invalidRowsOrColumns')] /** @dataProvider invalidRowsOrColumns */ public function testThrowsOnInvalidRowsOrColumns(string $row, int $column): void { @@ -789,6 +796,7 @@ public static function invalidRowsOrColumns(): iterable yield ['rolf', 2]; } + #[\PHPUnit\Framework\Attributes\DataProvider('invalidPositions')] /** @dataProvider invalidPositions */ public function testThrowsOnInvalidPositions(int $position): void { @@ -805,6 +813,7 @@ public static function invalidPositions(): iterable yield [10000]; } + #[\PHPUnit\Framework\Attributes\DataProvider('invalidCoordinates')] /** @dataProvider invalidCoordinates */ public function testThrowsOnInvalidCoordinates(string $coordinatesString): void { diff --git a/tests/Microplate/MicroplateSet/MicroplateSetABCDTest.php b/tests/Microplate/MicroplateSet/MicroplateSetABCDTest.php index 81fc89b..d98f67d 100644 --- a/tests/Microplate/MicroplateSet/MicroplateSetABCDTest.php +++ b/tests/Microplate/MicroplateSet/MicroplateSetABCDTest.php @@ -46,6 +46,7 @@ public function testSetLocationFromSetPositionFor12WellPlatesOutOfRangeTooLow(): $microplateSet->locationFromPosition($setPositionLowerThanMin, FlowDirection::COLUMN()); } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider12Well')] /** @dataProvider dataProvider12Well */ public function testSetLocationFromSetPositionFor12Wells(int $position, string $coordinatesString, string $plateID): void { @@ -91,6 +92,7 @@ public static function dataProvider12Well(): iterable ]; } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider96Well')] /** @dataProvider dataProvider96Well */ public function testSetLocationFromSetPositionFor96Wells(int $position, string $coordinatesString, string $plateID): void { diff --git a/tests/Microplate/MicroplateSet/MicroplateSetABTest.php b/tests/Microplate/MicroplateSet/MicroplateSetABTest.php index 6976d1a..a8c2af9 100644 --- a/tests/Microplate/MicroplateSet/MicroplateSetABTest.php +++ b/tests/Microplate/MicroplateSet/MicroplateSetABTest.php @@ -46,6 +46,7 @@ public function testSetLocationFromSetPositionFor12WellPlatesOutOfRangeTooLow(): $microplateSet->locationFromPosition($setPositionLowerThanMin, FlowDirection::COLUMN()); } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider12Well')] /** @dataProvider dataProvider12Well */ public function testSetLocationFromSetPositionFor12Wells(int $position, string $coordinatesString, string $plateID): void { @@ -91,6 +92,7 @@ public static function dataProvider12Well(): iterable ]; } + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider96Well')] /** @dataProvider dataProvider96Well */ public function testSetLocationFromSetPositionFor96Wells(int $position, string $coordinatesString, string $plateID): void { diff --git a/tests/NumberTest.php b/tests/NumberTest.php index d7e836d..1c9fbc4 100644 --- a/tests/NumberTest.php +++ b/tests/NumberTest.php @@ -15,6 +15,7 @@ final class NumberTest extends TestCase * @param float|int $current * @param float|int $expected */ + #[\PHPUnit\Framework\Attributes\DataProvider('clampProvider')] public function testClamp($min, $max, $current, $expected): void { self::assertSame($expected, Number::clamp($min, $max, $current)); diff --git a/tests/StringUtilTest.php b/tests/StringUtilTest.php index 9a1bd9c..7e654f5 100644 --- a/tests/StringUtilTest.php +++ b/tests/StringUtilTest.php @@ -13,6 +13,7 @@ final class StringUtilTest extends TestCase * * @param iterable $parts */ + #[\PHPUnit\Framework\Attributes\DataProvider('joinNonEmpty')] public function testJoinNonEmpty(string $expectedJoined, string $glue, iterable $parts): void { self::assertSame( @@ -29,6 +30,7 @@ public static function joinNonEmpty(): iterable yield ['a,b', ',', new Collection(['a', null, '', 'b'])]; } + #[\PHPUnit\Framework\Attributes\DataProvider('shortenFirstname')] /** @dataProvider shortenFirstname */ public function testShortenFirstname(string $expectedShortened, string $input): void { @@ -53,6 +55,7 @@ public static function shortenFirstname(): iterable * * @param array $expectedLines */ + #[\PHPUnit\Framework\Attributes\DataProvider('splitLines')] public function testSplitLines(array $expectedLines, string $input): void { self::assertSame($expectedLines, StringUtil::splitLines($input));