Skip to content

Commit

Permalink
Fix DateTime mutation tests
Browse files Browse the repository at this point in the history
  • Loading branch information
veewee committed Aug 30, 2024
1 parent 678c8dc commit 162448f
Show file tree
Hide file tree
Showing 9 changed files with 418 additions and 145 deletions.
198 changes: 101 additions & 97 deletions composer.lock

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions config/infection.json.dist
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
},
"DecrementInteger": {
"ignore": [
"Psl\\DataStructure\\PriorityQueue::peek"
"Psl\\DataStructure\\PriorityQueue::peek",
"Psl\\DateTime\\TemporalConvenienceMethodsTrait::since"
]
},
"FunctionCallRemoval": {
Expand All @@ -42,7 +43,8 @@
},
"IncrementInteger": {
"ignore": [
"Psl\\DataStructure\\PriorityQueue::peek"
"Psl\\DataStructure\\PriorityQueue::peek",
"Psl\\DateTime\\TemporalConvenienceMethodsTrait::since"
]
},
"LogicalNot": {
Expand All @@ -58,6 +60,7 @@
},
"Throw_": {
"ignore": [
"Psl\\DateTime\\DateTime::__construct",
"Psl\\File\\ReadHandle::__construct",
"Psl\\File\\WriteHandle::__construct",
"Psl\\File\\ReadWriteHandle::__construct",
Expand Down
21 changes: 10 additions & 11 deletions src/Psl/DateTime/DateTimeConvenienceMethodsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -398,17 +398,17 @@ public function plusMonths(int $months): static
return $this;
}

if (0 > $months) {
if ($months < 1) {
return $this->minusMonths(-$months);
}

$plus_years = Math\div($months, 12);
$months_left = $months - ($plus_years * 12);
$plus_years = Math\div($months, MONTHS_PER_YEAR);
$months_left = $months - ($plus_years * MONTHS_PER_YEAR);
$target_month = $this->getMonth() + $months_left;

if ($target_month > 12) {
if ($target_month > MONTHS_PER_YEAR) {
$plus_years++;
$target_month = $target_month - 12;
$target_month = $target_month - MONTHS_PER_YEAR;
}

$target_month_enum = Month::from($target_month);
Expand Down Expand Up @@ -438,17 +438,17 @@ public function minusMonths(int $months): static
return $this;
}

if (0 > $months) {
if ($months < 1) {
return $this->plusMonths(-$months);
}

$minus_years = Math\div($months, 12);
$months_left = $months - ($minus_years * 12);
$minus_years = Math\div($months, MONTHS_PER_YEAR);
$months_left = $months - ($minus_years * MONTHS_PER_YEAR);
$target_month = $this->getMonth() - $months_left;

if ($target_month <= 0) {
$minus_years++;
$target_month = 12 - Math\abs($target_month);
$target_month = MONTHS_PER_YEAR - Math\abs($target_month);
}

$target_month_enum = Month::from($target_month);
Expand Down Expand Up @@ -616,10 +616,9 @@ public function toString(null|DateStyle $date_style = null, null|TimeStyle $time
$timestamp = $this->getTimestamp();

/**
* @psalm-suppress InvalidOperand
* @psalm-suppress ImpureMethodCall
*/
return Internal\create_intl_date_formatter($date_style, $time_style, null, $timezone ?? $this->getTimezone(), $locale)
->format($timestamp->getSeconds() + ($timestamp->getNanoseconds() / NANOSECONDS_PER_SECOND));
->format($timestamp->getSeconds());
}
}
29 changes: 13 additions & 16 deletions src/Psl/DateTime/Duration.php
Original file line number Diff line number Diff line change
Expand Up @@ -674,27 +674,24 @@ public function toString(int $max_decimals = 3): string
$sec_sign = $this->seconds < 0 || $this->nanoseconds < 0 ? '-' : '';
$sec = Math\abs($this->seconds);

/** @var list<array{string, string}> $values */
$values = [
[((string) $this->hours), 'hour(s)'],
[((string) $this->minutes), 'minute(s)'],
[$sec_sign . ((string) $sec) . $decimal_part, 'second(s)'],
];

// $end is the sizeof($values), use static value for better performance.
$end = 3;
while ($end > 0 && $values[$end - 1][0] === '0') {
--$end;
$containsHours = $this->hours !== 0;
$containsMinutes = $this->minutes !== 0;
$concatenatedSeconds = $sec_sign . ((string) $sec) . $decimal_part;
$containsSeconds = $concatenatedSeconds !== '0';

/** @var list<string> $output */
$output = [];
if ($containsHours) {
$output[] = ((string) $this->hours) . ' hour(s)';
}

$start = 0;
while ($start < $end && $values[$start][0] === '0') {
++$start;
if ($containsMinutes || ($containsHours && $containsSeconds)) {
$output[] = ((string) $this->minutes) . ' minute(s)';
}

$output = [];
for ($i = $start; $i < $end; ++$i) {
$output[] = $values[$i][0] . ' ' . $values[$i][1];
if ($containsSeconds) {
$output[] = $concatenatedSeconds . ' second(s)';
}

return ([] === $output) ? '0 second(s)' : Str\join($output, ', ');
Expand Down
140 changes: 121 additions & 19 deletions tests/unit/DateTime/DateTimeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
namespace Psl\Tests\Unit\DateTime;

use PHPUnit\Framework\TestCase;
use Psl\DateTime\DateStyle;
use Psl\DateTime\DateTime;
use Psl\DateTime\Exception\UnexpectedValueException;
use Psl\DateTime\FormatPattern;
use Psl\DateTime\Meridiem;
use Psl\DateTime\Month;
use Psl\DateTime\TimeStyle;
use Psl\DateTime\Timezone;
use Psl\DateTime\Weekday;
use Psl\Json;
use Psl\Locale\Locale;

use function time;

Expand Down Expand Up @@ -59,18 +63,20 @@ public function testFromParts(): void

static::assertSame(Timezone::UTC, $datetime->getTimezone());
static::assertSame(2024, $datetime->getYear());
static::assertSame(24, $datetime->getYearShort());
static::assertSame(2, $datetime->getMonth());
static::assertSame(4, $datetime->getDay());
static::assertSame(Weekday::Sunday, $datetime->getWeekday());
static::assertSame(14, $datetime->getHours());
static::assertSame(0, $datetime->getMinutes());
static::assertSame(0, $datetime->getSeconds());
static::assertSame(1, $datetime->getNanoseconds());
static::assertSame([2024, 2, 4, 14, 0, 0, 1,], $datetime->getParts());
}

public function testFromPartsWithDefaults(): void
{
$datetime = DateTime::fromParts(Timezone::UTC, 2024, Month::February, 4, );
$datetime = DateTime::fromParts(Timezone::UTC, 2024, Month::February, 4,);

static::assertSame(Timezone::UTC, $datetime->getTimezone());
static::assertSame(2024, $datetime->getYear());
Expand All @@ -83,22 +89,71 @@ public function testFromPartsWithDefaults(): void
static::assertSame(0, $datetime->getNanoseconds());
}

public function testFromPartsWithInvalidComponent(): void
{

/**
* @dataProvider provideInvalidComponentParts
*/
public function testFromPartsWithInvalidComponent(
string $expectedMessage,
int $year,
int $month,
int $day,
int $hours,
int $minutes,
int $seconds,
int $nanoseconds
): void {
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Unexpected hours value encountered. Provided "999", but the calendar expects "15". Ensure the hour falls within a 24-hour day.');
$this->expectExceptionMessage($expectedMessage);

DateTime::fromParts(Timezone::UTC, 2024, Month::February, 4, 999, 0, 0, 1);
DateTime::fromParts(Timezone::UTC, $year, $month, $day, $hours, $minutes, $seconds, $nanoseconds);
}

public function fromString(): void
public static function provideInvalidComponentParts(): array
{
$datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0);
return [
['Unexpected year value encountered. Provided "0", but the calendar expects "1". Check the year for accuracy and ensure it\'s within the supported range.', 0, 1, 1, 0, 0, 0, 0],
['Unexpected month value encountered. Provided "0", but the calendar expects "12". Ensure the month is within the 1-12 range and matches the specific year context.', 2024, 0, 1, 0, 0, 0, 0],
['Unexpected day value encountered. Provided "0", but the calendar expects "31". Ensure the day is valid for the given month and year, considering variations like leap years.', 2024, 1, 0, 0, 0, 0, 0],
['Unexpected hours value encountered. Provided "-1", but the calendar expects "23". Ensure the hour falls within a 24-hour day.', 2024, 1, 1, -1, 0, 0, 0],
['Unexpected minutes value encountered. Provided "-1", but the calendar expects "59". Check the minutes value for errors and ensure it\'s within the 0-59 range.', 2024, 1, 1, 0, -1, 0, 0],
['Unexpected seconds value encountered. Provided "59", but the calendar expects "-1". Ensure the seconds are correct and within the 0-59 range.', 2024, 1, 1, 0, 0, -1, 0],
];
}

public function testFromString(): void
{
$timezone = Timezone::EuropeBrussels;
$datetime = DateTime::fromParts($timezone, 2024, Month::February, 4, 14, 0, 0, 0);

$string = $datetime->toString();
$parsed = DateTime::fromString($string);
$parsed = DateTime::fromString($string, timezone: $timezone);

static::assertEquals($datetime->getTimestamp(), $parsed->getTimestamp());
static::assertSame($datetime->getTimezone(), $parsed->getTimezone());
static::assertSame($string, $parsed->toString());
}

public function testToString(): void
{
$datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0);

static::assertSame('4 Feb 2024, 14:00:00', $datetime->toString());
static::assertSame('Sunday 4 February 2024 at 14:00:00', $datetime->toString(date_style: DateStyle::Full));
static::assertSame('4 Feb 2024, 14:00:00 Greenwich Mean Time', $datetime->toString(time_style: TimeStyle::Full));
static::assertSame('4 Feb 2024, 15:00:00', $datetime->toString(timezone: TimeZone::EuropeBrussels));
static::assertSame('4 feb 2024, 14:00:00', $datetime->toString(locale: Locale::DutchBelgium));
}

public function testFormat(): void
{
$datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0);

static::assertSame('4 Feb 2024, 14:00:00', $datetime->format());
static::assertSame('02/04/2024', $datetime->format(pattern: FormatPattern::American));
static::assertSame('02/04/2024', $datetime->format(pattern: FormatPattern::American->value));
static::assertSame('4 Feb 2024, 15:00:00', $datetime->format(timezone: TimeZone::EuropeBrussels));
static::assertSame('4 feb 2024, 14:00:00', $datetime->format(locale: Locale::DutchBelgium));
}

public function testParse(): void
Expand All @@ -121,7 +176,6 @@ public function testParseWithTimezone(): void

static::assertEquals($datetime->getTimestamp(), $parsed->getTimestamp());
static::assertSame($datetime->getTimezone(), $parsed->getTimezone());

}

public function testWithDate(): void
Expand Down Expand Up @@ -173,18 +227,32 @@ public function testGetEra()

public function testGetCentury()
{
$datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0);
static::assertSame(20, DateTime::fromParts(Timezone::default(), 1999, Month::February, 4, 14)->getCentury());
static::assertSame(21, DateTime::fromParts(Timezone::default(), 2000, Month::February, 4, 14)->getCentury());
}

static::assertSame(21, $datetime->getCentury());
public static function provideTwelveHours()
{
yield [0, 12, Meridiem::AnteMeridiem];
yield [1, 1, Meridiem::AnteMeridiem];
yield [2, 2, Meridiem::AnteMeridiem];
yield [11, 11, Meridiem::AnteMeridiem];
yield [12, 12, Meridiem::PostMeridiem];
yield [13, 1, Meridiem::PostMeridiem];
yield [14, 2, Meridiem::PostMeridiem];
yield [23, 11, Meridiem::PostMeridiem];
}

public function testGetTwelveHours()
/**
* @dataProvider provideTwelveHours
*/
public function testGetTwelveHours(int $hour, $expectedTwelveHour, $expectedMeridiem)
{
$datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, 14, 0, 0, 0);
$datetime = DateTime::fromParts(Timezone::default(), 2024, Month::February, 4, $hour, 0, 0, 0);
[$hours, $meridiem] = $datetime->getTwelveHours();

static::assertSame(2, $hours);
static::assertSame(Meridiem::PostMeridiem, $meridiem);
static::assertSame($expectedTwelveHour, $hours);
static::assertSame($expectedMeridiem, $meridiem);
}

public function testGetIsoWeek(): void
Expand Down Expand Up @@ -221,6 +289,12 @@ public function testPlusMethods(): void
$new = $datetime->plusMonths(1);
static::assertSame(3, $new->getMonth());

$new = $datetime->plusMonths(0);
static::assertSame($datetime, $new);

$new = $datetime->plusMonths(-1);
static::assertSame(1, $new->getMonth());

$new = $datetime->plusDays(1);
static::assertSame(5, $new->getDay());

Expand Down Expand Up @@ -285,6 +359,12 @@ public function testMinusMethods(): void
$new = $datetime->minusMonths(1);
static::assertSame(1, $new->getMonth());

$new = $datetime->minusMonths(0);
static::assertSame($datetime, $new);

$new = $datetime->minusMonths(-1);
static::assertSame(3, $new->getMonth());

$new = $datetime->minusDays(1);
static::assertSame(3, $new->getDay());

Expand Down Expand Up @@ -412,9 +492,31 @@ public function testWithTime()
$date = DateTime::todayAt(14, 0);
$new = $date->withTime(15, 0);

self::assertSame(15, $new->getHours());
self::assertSame(0, $new->getMinutes());
self::assertSame(0, $new->getSeconds());
self::assertSame(0, $new->getNanoseconds());
static::assertSame(15, $new->getHours());
static::assertSame(0, $new->getMinutes());
static::assertSame(0, $new->getSeconds());
static::assertSame(0, $new->getNanoseconds());
}

public function testTimezoneInfo()
{
$timeZone = Timezone::EuropeBrussels;
$date = DateTime::fromParts($timeZone, 2024, 01, 01);

static::assertSame(!$timeZone->getDaylightSavingTimeOffset($date)->isZero(), $date->isDaylightSavingTime());
static::assertEquals($timeZone->getOffset($date), $date->getTimezoneOffset());
}

public function testConvertTimeZone()
{
$date = DateTime::fromParts(Timezone::EuropeBrussels, 2024, 01, 01, 1);
$converted = $date->convertToTimezone($london = Timezone::EuropeLondon);

static::assertSame($london, $converted->getTimezone());
static::assertSame($date->getTimestamp(), $converted->getTimestamp());
static::assertSame($date->getYear(), $converted->getYear());
static::assertSame($date->getMonth(), $converted->getMonth());
static::assertSame($date->getDay(), $converted->getDay());
static::assertSame(0, $converted->getHours());
}
}
16 changes: 16 additions & 0 deletions tests/unit/DateTime/DurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ public function testGetters(): void
static::assertEquals([1, 2, 3, 4], $t->getParts());
}

public function testNamedConstructors()
{
static::assertSame(168.0, DateTime\Duration::weeks(1)->getTotalHours());
static::assertSame(24.0, DateTime\Duration::days(1)->getTotalHours());
static::assertSame(1.0, DateTime\Duration::hours(1)->getTotalHours());
static::assertSame(1.0, DateTime\Duration::minutes(1)->getTotalMinutes());
static::assertSame(1.0, DateTime\Duration::seconds(1)->getTotalSeconds());
static::assertSame(1.0, DateTime\Duration::milliseconds(1)->getTotalMilliseconds());
static::assertSame(1.0, DateTime\Duration::microseconds(1)->getTotalMicroseconds());
static::assertSame(1, DateTime\Duration::nanoseconds(1)->getNanoseconds());
static::assertSame(0.0, DateTime\Duration::zero(1)->getTotalSeconds());
}

public function provideGetTotalHours(): array
{
return [
Expand Down Expand Up @@ -237,6 +250,9 @@ public function testPositiveNegative(int $h, int $m, int $s, int $ns, int $expec
public static function provideCompare(): array
{
return [
[DateTime\Duration::seconds(20), DateTime\Duration::seconds(10), Order::Greater],
[DateTime\Duration::seconds(10), DateTime\Duration::seconds(20), Order::Less],
[DateTime\Duration::seconds(10), DateTime\Duration::seconds(10), Order::Equal],
[DateTime\Duration::hours(1), DateTime\Duration::minutes(42), Order::Greater],
[DateTime\Duration::minutes(2), DateTime\Duration::seconds(120), Order::Equal],
[DateTime\Duration::zero(), DateTime\Duration::nanoseconds(1), Order::Less],
Expand Down
Loading

0 comments on commit 162448f

Please sign in to comment.