Skip to content

Commit 324c55c

Browse files
authored
(Added) Comprehensive test suite enhancement for StringManipulation library
This merge request introduces a significant enhancement to the project's test suite, increasing the total number of tests from 125 to 166 and assertions to 1126. The changes add comprehensive test coverage for all functions within the `StringManipulation` library, with a special focus on the `nameFix` method. This effort improves code quality, ensures robustness against a wide range of inputs, and increases the mutation testing score from 90% to 92%.
1 parent f7e2331 commit 324c55c

15 files changed

Lines changed: 2521 additions & 106 deletions

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
"test:lint": "parallel-lint --exclude vendor --show-deprecated .",
9999
"test:phan": "phan --no-progress-bar",
100100
"test:phpmd": "phpmd src,tests text phpmd.xml",
101-
"test:phpstan": "phpstan analyse --no-progress --no-interaction",
101+
"test:phpstan": "phpstan analyse --memory-limit=-1 --no-progress --no-interaction",
102102
"test:phpunit": "phpunit --no-coverage --no-logging",
103103
"test:psalm": "psalm --no-cache --no-progress --show-info=false",
104104
"test:rector": "rector --dry-run",

tests/Unit/IsValidDateTest.php

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,151 @@ public function testInvalidDates(string $date, string $format): void
198198
{
199199
self::assertFalse(StringManipulation::isValidDate($date, $format));
200200
}
201+
202+
203+
/**
204+
* Test edge cases and boundary conditions for date validation.
205+
*/
206+
public function testDateValidationEdgeCases(): void
207+
{
208+
// Leap year edge cases
209+
self::assertTrue(StringManipulation::isValidDate('2000-02-29', 'Y-m-d')); // Leap year (divisible by 400)
210+
self::assertTrue(StringManipulation::isValidDate('2004-02-29', 'Y-m-d')); // Leap year (divisible by 4)
211+
self::assertFalse(StringManipulation::isValidDate('1900-02-29', 'Y-m-d')); // Not leap year (divisible by 100, not 400)
212+
self::assertFalse(StringManipulation::isValidDate('2023-02-29', 'Y-m-d')); // Not leap year
213+
214+
// Month boundary cases
215+
self::assertTrue(StringManipulation::isValidDate('2023-01-31', 'Y-m-d')); // January 31 days
216+
self::assertFalse(StringManipulation::isValidDate('2023-02-31', 'Y-m-d')); // February doesn't have 31 days
217+
self::assertTrue(StringManipulation::isValidDate('2023-03-31', 'Y-m-d')); // March 31 days
218+
self::assertFalse(StringManipulation::isValidDate('2023-04-31', 'Y-m-d')); // April only has 30 days
219+
self::assertTrue(StringManipulation::isValidDate('2023-05-31', 'Y-m-d')); // May 31 days
220+
self::assertFalse(StringManipulation::isValidDate('2023-06-31', 'Y-m-d')); // June only has 30 days
221+
self::assertTrue(StringManipulation::isValidDate('2023-12-31', 'Y-m-d')); // December 31 days
222+
223+
// Time boundary cases
224+
self::assertTrue(StringManipulation::isValidDate('00:00:00', 'H:i:s')); // Midnight
225+
self::assertTrue(StringManipulation::isValidDate('23:59:59', 'H:i:s')); // Last second of day
226+
self::assertFalse(StringManipulation::isValidDate('24:00:00', 'H:i:s')); // Invalid hour
227+
self::assertFalse(StringManipulation::isValidDate('23:60:00', 'H:i:s')); // Invalid minute
228+
self::assertFalse(StringManipulation::isValidDate('23:59:60', 'H:i:s')); // Invalid second
229+
230+
// Year boundary cases
231+
self::assertTrue(StringManipulation::isValidDate('0001-01-01', 'Y-m-d')); // Minimum year
232+
self::assertTrue(StringManipulation::isValidDate('9999-12-31', 'Y-m-d')); // Maximum year
233+
self::assertFalse(StringManipulation::isValidDate('0000-01-01', 'Y-m-d')); // Year 0 doesn't exist
234+
235+
// Complex format edge cases
236+
self::assertTrue(StringManipulation::isValidDate('31-12-2023 23:59:59', 'd-m-Y H:i:s'));
237+
self::assertFalse(StringManipulation::isValidDate('32-12-2023 23:59:59', 'd-m-Y H:i:s'));
238+
239+
// Different separators
240+
self::assertTrue(StringManipulation::isValidDate('2023/12/31', 'Y/m/d'));
241+
self::assertTrue(StringManipulation::isValidDate('2023.12.31', 'Y.m.d'));
242+
self::assertTrue(StringManipulation::isValidDate('31.12.2023', 'd.m.Y'));
243+
}
244+
245+
246+
/**
247+
* Test performance and stress scenarios for date validation.
248+
*/
249+
public function testDateValidationPerformance(): void
250+
{
251+
// Large batch of date validations
252+
$dates = [];
253+
for ($year = 2020; $year <= 2025; ++$year) {
254+
for ($month = 1; $month <= 12; ++$month) {
255+
for ($day = 1; $day <= 28; ++$day) { // Safe range for all months
256+
$dates[] = sprintf('%04d-%02d-%02d', $year, $month, $day);
257+
}
258+
}
259+
}
260+
261+
$startTime = microtime(true);
262+
$validCount = 0;
263+
foreach ($dates as $date) {
264+
if (StringManipulation::isValidDate($date, 'Y-m-d')) {
265+
++$validCount;
266+
}
267+
}
268+
269+
$duration = microtime(true) - $startTime;
270+
271+
self::assertEquals(count($dates), $validCount); // All should be valid
272+
self::assertLessThan(2.0, $duration, 'Batch date validation should complete efficiently');
273+
274+
// Stress test with complex formats
275+
$complexDates = [
276+
'2023-12-31 23:59:59',
277+
'31-12-2023 00:00:00',
278+
'2023/01/01 12:30:45',
279+
'01.01.2023 06:15:30',
280+
];
281+
282+
$formats = [
283+
'Y-m-d H:i:s',
284+
'd-m-Y H:i:s',
285+
'Y/m/d H:i:s',
286+
'd.m.Y H:i:s',
287+
];
288+
289+
$startTime = microtime(true);
290+
foreach ($complexDates as $index => $date) {
291+
self::assertTrue(StringManipulation::isValidDate($date, $formats[$index]));
292+
}
293+
294+
$duration = microtime(true) - $startTime;
295+
self::assertLessThan(0.1, $duration, 'Complex format validation should be fast');
296+
}
297+
298+
299+
/**
300+
* Test negative flow scenarios for date validation.
301+
*/
302+
public function testDateValidationNegativeFlow(): void
303+
{
304+
// Malformed dates
305+
$malformedDates = [
306+
['', 'Y-m-d'],
307+
['not-a-date', 'Y-m-d'],
308+
['2023-13-01', 'Y-m-d'], // Invalid month
309+
['2023-00-01', 'Y-m-d'], // Invalid month
310+
['2023-01-00', 'Y-m-d'], // Invalid day
311+
['2023-01-32', 'Y-m-d'], // Invalid day
312+
['2023/01/01', 'Y-m-d'], // Wrong separator
313+
['2023-1-1', 'Y-m-d'], // Missing zero padding
314+
];
315+
316+
foreach ($malformedDates as [$date, $format]) {
317+
self::assertFalse(StringManipulation::isValidDate($date, $format), sprintf("Date '%s' should be invalid for format '%s'", $date, $format));
318+
}
319+
320+
// Time validation edge cases
321+
$invalidTimes = [
322+
['25:00:00', 'H:i:s'], // Hour too high
323+
['12:60:00', 'H:i:s'], // Minute too high
324+
['12:30:60', 'H:i:s'], // Second too high
325+
['-1:30:00', 'H:i:s'], // Negative hour
326+
['12:-1:00', 'H:i:s'], // Negative minute
327+
['12:30:-1', 'H:i:s'], // Negative second
328+
];
329+
330+
foreach ($invalidTimes as [$time, $format]) {
331+
self::assertFalse(StringManipulation::isValidDate($time, $format), sprintf("Time '%s' should be invalid for format '%s'", $time, $format));
332+
}
333+
334+
// Format mismatch scenarios
335+
$formatMismatches = [
336+
['2023-01-01', 'd-m-Y'], // Date format doesn't match
337+
['01-01-2023', 'Y-m-d'], // Date format doesn't match
338+
['2023-01-01 12:30:00', 'Y-m-d'], // Extra time component
339+
['12:30:00', 'Y-m-d H:i:s'], // Missing date component
340+
['2023', 'Y-m-d'], // Incomplete date
341+
['01-01', 'Y-m-d'], // Incomplete date
342+
];
343+
344+
foreach ($formatMismatches as [$date, $format]) {
345+
self::assertFalse(StringManipulation::isValidDate($date, $format), sprintf("Date '%s' should not match format '%s'", $date, $format));
346+
}
347+
}
201348
}

tests/Unit/IsValidHourTest.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,6 @@ public function testIsValidHour(int $hour, bool $expectedResult): void
7373
{
7474
$reflectionMethod = (new ReflectionClass(StringManipulation::class))->getMethod('isValidHour');
7575

76-
/**
77-
* @noinspection PhpExpressionResultUnusedInspection
78-
*
79-
* @psalm-suppress UnusedMethodCall
80-
*/
81-
$reflectionMethod->setAccessible(true);
82-
8376
$result = $reflectionMethod->invoke(null, $hour);
8477

8578
self::assertSame($expectedResult, $result);

tests/Unit/IsValidMinuteTest.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,6 @@ public function testIsValidMinute(int $minute, bool $expectedResult): void
6565
{
6666
$reflectionMethod = (new ReflectionClass(StringManipulation::class))->getMethod('isValidMinute');
6767

68-
/**
69-
* @noinspection PhpExpressionResultUnusedInspection
70-
*
71-
* @psalm-suppress UnusedMethodCall
72-
*/
73-
$reflectionMethod->setAccessible(true);
74-
7568
$result = $reflectionMethod->invoke(null, $minute);
7669

7770
self::assertSame($expectedResult, $result);

tests/Unit/IsValidSecondTest.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,6 @@ public function testIsValidSecond(int $second, bool $expectedResult): void
6565
{
6666
$reflectionMethod = (new ReflectionClass(StringManipulation::class))->getMethod('isValidSecond');
6767

68-
/**
69-
* @noinspection PhpExpressionResultUnusedInspection
70-
*
71-
* @psalm-suppress UnusedMethodCall
72-
*/
73-
$reflectionMethod->setAccessible(true);
74-
7568
$result = $reflectionMethod->invoke(null, $second);
7669

7770
self::assertSame($expectedResult, $result);

tests/Unit/IsValidTimePartTest.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,6 @@ public function testIsValidTimePart(array $timeParts, bool $expectedResult): voi
7474
{
7575
$reflectionMethod = (new ReflectionClass(StringManipulation::class))->getMethod('isValidTimePart');
7676

77-
/**
78-
* @noinspection PhpExpressionResultUnusedInspection
79-
*
80-
* @psalm-suppress UnusedMethodCall
81-
*/
82-
$reflectionMethod->setAccessible(true);
83-
8477
$result = $reflectionMethod->invoke(null, $timeParts);
8578

8679
self::assertSame($expectedResult, $result);

0 commit comments

Comments
 (0)