Skip to content

Commit 7361bc3

Browse files
committed
Improve Interval tests and format
1 parent 8590a04 commit 7361bc3

8 files changed

Lines changed: 233 additions & 63 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ All Notable changes to `bakame/tokei` will be documented in this file.
1515
- `IntervalSet::nearest`
1616
- `Interval::fromFormat`
1717
- `Interval::roundTo`
18+
- `Interval::roundDurationTo`
1819
- `Duration::fromDateInterval`
1920
- `Duration::fromFormat`
2021
- `Duration::format`

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,9 @@ Interval::expand(Duration $duration): self
576576
Interval::shift(Duration $duration): self
577577
Interval::shiftBound(Duration $duration, Bound $from): self
578578
Interval::lasting(Duration $duration, Bound $from): self
579+
Interval::roundTo(Unit $unit, SnapMode $mode): self
580+
Interval::roundDurationTo(Unit $unit, SnapMode $mode, Bound $anchor = Bound::Start): self
581+
Interval::lasting(Duration $duration, Bound $from): self
579582
Interval::complement(): self
580583
```
581584

src/EventTest.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Bakame\Tokei;
66

77
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\DataProvider;
89
use PHPUnit\Framework\TestCase;
910

1011
#[CoversClass(Event::class)]
@@ -64,4 +65,96 @@ public function testWithAttributesReturnsNewTask(): void
6465
self::assertSame(['John'], $updated->identifiers->all());
6566
self::assertEquals($event->at, $updated->at);
6667
}
68+
69+
#[DataProvider('validFormats')]
70+
public function testCanCreateEventFromFormattedString(
71+
string $input,
72+
string $expectedTime,
73+
string $expectedIdentifier,
74+
): void {
75+
$event = Event::fromFormat($input);
76+
77+
self::assertSame($expectedTime, $event->at->format());
78+
self::assertSame($expectedIdentifier, $event->identifiers->formatted());
79+
}
80+
81+
/**
82+
* @return iterable<non-empty-string, array{0: string, 1: non-empty-string, 2: string}>
83+
*/
84+
public static function validFormats(): iterable
85+
{
86+
yield 'no spaces' => [
87+
'12:00:23;larry-king',
88+
'12:00:23',
89+
'larry-king',
90+
];
91+
92+
yield 'space after separator' => [
93+
'12:00:23; larry-king',
94+
'12:00:23',
95+
'larry-king',
96+
];
97+
98+
yield 'space before separator' => [
99+
'12:00:23 ;larry-king',
100+
'12:00:23',
101+
'larry-king',
102+
];
103+
104+
yield 'spaces around separator' => [
105+
'12:00:23 ; larry-king',
106+
'12:00:23',
107+
'larry-king',
108+
];
109+
110+
yield 'multiple spaces around separator' => [
111+
'12:00:23 ; larry-king',
112+
'12:00:23',
113+
'larry-king',
114+
];
115+
116+
yield 'missing identifier' => [
117+
'12:00:23;',
118+
'12:00:23',
119+
'',
120+
];
121+
122+
yield 'multiple spaces between identifier separator' => [
123+
'12:00:23 ; larry-king , junior',
124+
'12:00:23',
125+
'larry-king,junior',
126+
];
127+
}
128+
129+
#[DataProvider('invalidFormats')]
130+
public function testRejectsInvalidFormats(string $input): void
131+
{
132+
$this->expectException(TokeiException::class);
133+
134+
Event::fromFormat($input);
135+
}
136+
137+
/**
138+
* @return iterable<non-empty-string, array{0: string}>
139+
*/
140+
public static function invalidFormats(): iterable
141+
{
142+
yield 'empty string' => [''];
143+
144+
yield 'missing separator' => [
145+
'12:00:23 larry-king',
146+
];
147+
148+
yield 'missing time' => [
149+
';larry-king',
150+
];
151+
152+
yield 'invalid time' => [
153+
'25:00:00;larry-king',
154+
];
155+
156+
yield 'extra separator' => [
157+
'12:00:23;larry;king',
158+
];
159+
}
67160
}

src/Identifiers.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,20 @@ private static function sanitize(mixed $value): string
5555
private array $items;
5656

5757
/**
58-
* @param InputIdentifiers $items
58+
* @param InputIdentifiers ...$items
5959
*
6060
* @throws TemporalException
6161
*/
62-
public function __construct(Identifiers|HasIdentifiers|iterable|string $items = [])
62+
public function __construct(Identifiers|HasIdentifiers|iterable|string ...$items)
6363
{
64-
$this->items = self::filterIdentifiers($items);
64+
$found = [];
65+
foreach ($items as $item) {
66+
foreach (self::filterIdentifiers($item) as $value) {
67+
$found[] = $value;
68+
}
69+
}
70+
71+
$this->items = array_values(array_unique($found));
6572
}
6673

6774
/**

src/Interval.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,26 +271,38 @@ public function lasting(Duration $duration, Bound $from): self
271271
*/
272272
public function expand(Duration $duration): self
273273
{
274-
return self::between($this->start->shift($duration->negated()), $this->end->shift($duration));
274+
$new = self::between($this->start->shift($duration->negated()), $this->end->shift($duration));
275+
276+
return $new->equals($this) ? $this : $new;
275277
}
276278

277279
public function roundTo(Unit $unit, SnapMode $mode = SnapMode::Nearest): self
278280
{
279-
$rounded = self::between($this->start->roundTo($unit, $mode), $this->end->roundTo($unit, $mode));
281+
$new = self::between($this->start->roundTo($unit, $mode), $this->end->roundTo($unit, $mode));
282+
283+
return $new->equals($this) ? $this : $new;
284+
}
285+
286+
public function roundDurationTo(Unit $unit, SnapMode $mode = SnapMode::Nearest, Bound $anchor = Bound::Start): self
287+
{
288+
$duration = $this->duration->roundTo($unit, $mode);
289+
$new = Bound::Start === $anchor ? self::since($this->start, $duration) : self::until($this->end, $duration);
280290

281-
return $rounded->equals($this) ? $this : $rounded;
291+
return $new->equals($this) ? $this : $new;
282292
}
283293

284294
/**
285295
* @throws InvalidDuration
286296
*/
287297
public function complement(): self
288298
{
289-
return match ($this->type) {
299+
$new = match ($this->type) {
290300
IntervalType::Collapsed => self::circular($this->start),
291301
IntervalType::Circular => self::collapsed($this->start),
292302
default => self::between($this->end, $this->start),
293303
};
304+
305+
return $new->equals($this) ? $this : $new;
294306
}
295307

296308
/**

src/IntervalFormat.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,10 @@ enum IntervalFormat
2626
case Iso8601DurationEnd;
2727
case Iso8601StartEnd;
2828
case Iso8601;
29-
case Canonical;
3029

3130
private const string REGEXP_ISO80000 = '/^\[(?<start>[^,)]*),(?<end>[^,)]*)\)$/';
3231
private const string REGEXP_BOURBAKI = '/^\[(?<start>[^,\[]*),(?<end>[^,\[]*)\[$/';
3332
private const string REGEXP_ISO8601 = '/^(?<start>[^\/]+)\/(?<end>[^\/]+)$/';
34-
private const string REGEXP_CANONICAL = '/^(?<start>[^\/]+)\/(?<end>.+)\[\)$/';
3533

3634
/**
3735
* @return non-empty-string
@@ -48,7 +46,6 @@ public function encode(Interval $interval, ?Unit $unit = null): string
4846
self::Iso8601StartEnd => $start.'/'.$end,
4947
self::Iso80000 => '['.$start.','.$end.')',
5048
self::Bourbaki => '['.$start.','.$end.'[',
51-
self::Canonical => $start.'/'.$interval->duration->format().'[)',
5249
};
5350
}
5451

@@ -61,7 +58,6 @@ public function decode(string $data, ?Unit $unit = null): Interval
6158
$pattern = match ($this) {
6259
self::Bourbaki => self::REGEXP_BOURBAKI,
6360
self::Iso80000 => self::REGEXP_ISO80000,
64-
self::Canonical => self::REGEXP_CANONICAL,
6561
default => self::REGEXP_ISO8601,
6662
};
6763

@@ -176,7 +172,6 @@ private function parseIso8601Interval(string $start, string $end, string $format
176172
private function supportsStartDuration(): bool
177173
{
178174
return match ($this) {
179-
self::Canonical,
180175
self::Iso8601,
181176
self::Iso8601StartDuration => true,
182177
default => false,

src/IntervalTest.php

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -951,27 +951,6 @@ public function test_interval_formatting_improved(): void
951951
)->format(IntervalFormat::Bourbaki, Unit::Minute));
952952
}
953953

954-
/**
955-
* @param non-empty-string $notation
956-
*/
957-
#[DataProvider('invalidCanonicalNotation')]
958-
public function test_it_can_not_decode_an_invalid_canonical_string(string $notation): void
959-
{
960-
$this->expectException(TimeException::class);
961-
962-
Interval::fromFormat('11:00:00/PT3M0.5S[]', IntervalFormat::Canonical);
963-
}
964-
965-
/**
966-
* @return iterable<non-empty-string, array{notation: non-empty-string}>
967-
*/
968-
public static function invalidCanonicalNotation(): iterable
969-
{
970-
yield 'unsupported boundaries' => ['notation' => '11:00:00/PT3M0.5S[]'];
971-
yield 'missing boundaries' => ['notation' => '11:00:00/PT3M0.5S'];
972-
yield 'wrong notation' => ['notation' => '[11:00:00,12:00:00)'];
973-
}
974-
975954
public function test_fixing_split_at_overflow(): void
976955
{
977956
$interval = Interval::between(Time::at(22), Time::at(3));
@@ -1003,4 +982,36 @@ public function test_roundto_interval(): void
1003982
self::assertTrue($updated->start->equals(Time::at(22, 14)));
1004983
self::assertTrue($updated->end->equals(Time::at(3, 42)));
1005984
}
985+
986+
public function test_roundDurationTo_interval_with_start_anchor(): void
987+
{
988+
$interval = Interval::between(
989+
Time::at(22, 13, 35),
990+
Time::at(3, 42, 27)
991+
);
992+
993+
self::assertSame($interval, $interval->roundDurationTo(Unit::Second));
994+
995+
$updated = $interval->roundDurationTo(Unit::Minute);
996+
997+
self::assertNotEquals($updated, $interval);
998+
self::assertTrue($updated->start->equals($interval->start));
999+
self::assertFalse($updated->end->equals($interval->end));
1000+
}
1001+
1002+
public function test_roundDurationTo_interval_with_end_anchor(): void
1003+
{
1004+
$interval = Interval::between(
1005+
Time::at(22, 13, 35),
1006+
Time::at(3, 42, 27)
1007+
);
1008+
1009+
self::assertSame($interval, $interval->roundDurationTo(Unit::Second));
1010+
1011+
$updated = $interval->roundDurationTo(Unit::Minute, SnapMode::Nearest, Bound::End);
1012+
1013+
self::assertNotEquals($updated, $interval);
1014+
self::assertTrue($updated->end->equals($interval->end));
1015+
self::assertFalse($updated->start->equals($interval->start));
1016+
}
10061017
}

0 commit comments

Comments
 (0)