Skip to content

Commit 65271f8

Browse files
committed
New tests.
1 parent 60271f6 commit 65271f8

File tree

2 files changed

+317
-11
lines changed

2 files changed

+317
-11
lines changed

src/Structs/Slice.php

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public static function toSlice($s): Slice
3838
}
3939

4040
if (!self::isSliceString($s)) {
41-
throw new ValueError("Invalid slice: \"{$s}\".");
41+
$str = \is_scalar($s) ? "{$s}" : gettype($s);
42+
throw new ValueError("Invalid slice: \"{$str}\".");
4243
}
4344

4445
$slice = self::parseSliceString($s);
@@ -80,6 +81,33 @@ public static function isSliceString($s): bool
8081
return !(\count($slice) < 1 || \count($slice) > 3);
8182
}
8283

84+
/**
85+
* @param mixed $s
86+
*
87+
* @return bool
88+
*/
89+
public static function isSliceArray($s): bool
90+
{
91+
if (!\is_array($s)) {
92+
return false;
93+
}
94+
95+
if (!(\count($s) >= 0 && \count($s) <= 3)) {
96+
return false;
97+
}
98+
99+
foreach ($s as $key => $item) {
100+
if (\is_string($key)) {
101+
return false;
102+
}
103+
if ($item !== null && (!\is_numeric($item) || \is_float($item + 0))) {
104+
return false;
105+
}
106+
}
107+
108+
return true;
109+
}
110+
83111
/**
84112
* @param int|null $start
85113
* @param int|null $end
@@ -93,11 +121,11 @@ public function __construct(?int $start = null, ?int $end = null, ?int $step = n
93121
}
94122

95123
/**
96-
* @param int $containerLength
124+
* @param int $containerSize
97125
*
98126
* @return NormalizedSlice
99127
*/
100-
public function normalize(int $containerLength): NormalizedSlice
128+
public function normalize(int $containerSize): NormalizedSlice
101129
{
102130
// TODO: Need refactor
103131
$step = $this->step ?? 1;
@@ -108,25 +136,25 @@ public function normalize(int $containerLength): NormalizedSlice
108136

109137
$defaultEnd = ($step < 0 && $this->end === null) ? -1 : null;
110138

111-
$start = $this->start ?? ($step > 0 ? 0 : $containerLength - 1);
112-
$end = $this->end ?? ($step > 0 ? $containerLength : -1);
139+
$start = $this->start ?? ($step > 0 ? 0 : $containerSize - 1);
140+
$end = $this->end ?? ($step > 0 ? $containerSize : -1);
113141

114142
$start = intval(round($start));
115143
$end = intval(round($end));
116144
$step = intval(round($step));
117145

118-
$start = Util::normalizeIndex($start, $containerLength, false);
119-
$end = Util::normalizeIndex($end, $containerLength, false);
146+
$start = Util::normalizeIndex($start, $containerSize, false);
147+
$end = Util::normalizeIndex($end, $containerSize, false);
120148

121-
if ($step > 0 && $start >= $containerLength) {
122-
$start = $end = $containerLength - 1;
149+
if ($step > 0 && $start >= $containerSize) {
150+
$start = $end = $containerSize - 1;
123151
} elseif ($step < 0 && $start < 0) {
124152
$start = $end = 0;
125153
$defaultEnd = 0;
126154
}
127155

128-
$start = $this->squeezeInBounds($start, 0, $containerLength - 1);
129-
$end = $this->squeezeInBounds($end, $step > 0 ? 0 : -1, $containerLength);
156+
$start = $this->squeezeInBounds($start, 0, $containerSize - 1);
157+
$end = $this->squeezeInBounds($end, $step > 0 ? 0 : -1, $containerSize);
130158

131159
if (($step > 0 && $end < $start) || ($step < 0 && $end > $start)) {
132160
$end = $start;
@@ -150,6 +178,9 @@ public function toString(): string
150178
*/
151179
private static function parseSliceString(string $s): array
152180
{
181+
if ($s === '') {
182+
return [];
183+
}
153184
return array_map(fn($x) => trim($x) === '' ? null : \intval(trim($x)), \explode(':', $s));
154185
}
155186

tests/unit/Structs/SliceTest.php

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Smoren\ArrayView\Tests\Unit\Structs;
6+
7+
use Smoren\ArrayView\Exceptions\ValueError;
8+
use Smoren\ArrayView\Structs\NormalizedSlice;
9+
use Smoren\ArrayView\Structs\Slice;
10+
11+
class SliceTest extends \Codeception\Test\Unit
12+
{
13+
/**
14+
* @dataProvider dataProviderForTrue
15+
*/
16+
public function testIsSliceTrue(string $input)
17+
{
18+
$this->assertTrue(Slice::isSlice($input));
19+
$this->assertTrue(Slice::isSliceString($input));
20+
}
21+
22+
/**
23+
* @dataProvider dataProviderForFalse
24+
*/
25+
public function testIsSliceFalse($input)
26+
{
27+
$this->assertFalse(Slice::isSlice($input));
28+
$this->assertFalse(Slice::isSliceString($input));
29+
}
30+
31+
/**
32+
* @dataProvider dataProviderForFalse
33+
*/
34+
public function testSliceError($input)
35+
{
36+
$this->expectException(ValueError::class);
37+
$strInput = \is_scalar($input) ? "{$input}" : gettype($input);
38+
$this->expectExceptionMessage("Invalid slice: \"{$strInput}\"");
39+
40+
Slice::toSlice($input);
41+
}
42+
43+
/**
44+
* @dataProvider dataProviderForToSlice
45+
*/
46+
public function testToSlice($input, array $expected)
47+
{
48+
$actual = Slice::toSlice($input);
49+
$expectedSlice = new Slice(...$expected);
50+
51+
$this->assertSame($expectedSlice->start, $actual->start);
52+
$this->assertSame($expectedSlice->end, $actual->end);
53+
$this->assertSame($expectedSlice->step, $actual->step);
54+
}
55+
56+
/**
57+
* @dataProvider dataProviderForSliceToString
58+
*/
59+
public function testSliceToString(string $input, string $expected)
60+
{
61+
$slice = Slice::toSlice($input);
62+
$this->assertSame($expected, $slice->toString());
63+
}
64+
65+
/**
66+
* @dataProvider dataProviderForSliceNormalize
67+
*/
68+
public function testSliceNormalize(string $input, int $size, string $expected, array $expectedIndexes)
69+
{
70+
$slice = Slice::toSlice($input);
71+
$normalizedSlice = $slice->normalize($size);
72+
73+
$this->assertInstanceOf(NormalizedSlice::class, $normalizedSlice);
74+
$this->assertSame($expected, $normalizedSlice->toString());
75+
$this->assertSame($expectedIndexes, [...$normalizedSlice]);
76+
}
77+
78+
/**
79+
* @dataProvider dataProviderForIsSliceArrayTrue
80+
*/
81+
public function testIsSliceArrayTrue(array $input)
82+
{
83+
$this->assertTrue(Slice::isSliceArray($input));
84+
}
85+
86+
/**
87+
* @dataProvider dataProviderForIsSliceArrayFalse
88+
*/
89+
public function testIsSliceArrayFalse($input)
90+
{
91+
$this->assertFalse(Slice::isSliceArray($input));
92+
}
93+
94+
public function dataProviderForTrue(): array
95+
{
96+
return [
97+
[':'],
98+
['::'],
99+
['0:'],
100+
['1:'],
101+
['-1:'],
102+
['0::'],
103+
['1::'],
104+
['-1::'],
105+
[':0'],
106+
[':1'],
107+
[':-1'],
108+
[':0:'],
109+
[':1:'],
110+
[':-1:'],
111+
['0:0:'],
112+
['1:1:'],
113+
['-1:-1:'],
114+
['1:1:-1'],
115+
['-1:-1:-1'],
116+
['1:2:3'],
117+
];
118+
}
119+
120+
public function dataProviderForFalse(): array
121+
{
122+
return [
123+
[''],
124+
['0'],
125+
['1'],
126+
['1:::'],
127+
[':1::'],
128+
['::1:'],
129+
[':::1'],
130+
['test'],
131+
['[::]'],
132+
['a:b:c'],
133+
[0],
134+
[1],
135+
[1.1],
136+
[true],
137+
[false],
138+
[null],
139+
[[]],
140+
[[1, 2, 3]],
141+
[[null]],
142+
[new \ArrayObject([])],
143+
[['a' => 1]],
144+
];
145+
}
146+
147+
public function dataProviderForToSlice(): array
148+
{
149+
return [
150+
[':', [null, null, null]],
151+
['::', [null, null, null]],
152+
['0:', [0, null, null]],
153+
['1:', [1, null, null]],
154+
['-1:', [-1, null, null]],
155+
['0::', [0, null, null]],
156+
['1::', [1, null, null]],
157+
['-1::', [-1, null, null]],
158+
[':0', [null, 0, null]],
159+
[':1', [null, 1, null]],
160+
[':-1', [null, -1, null]],
161+
[':0:', [null, 0, null]],
162+
[':1:', [null, 1, null]],
163+
[':-1:', [null, -1, null]],
164+
['0:0:', [0, 0, null]],
165+
['1:1:', [1, 1, null]],
166+
['-1:-1:', [-1, -1, null]],
167+
['1:1:-1', [1, 1, -1]],
168+
['-1:-1:-1', [-1, -1, -1]],
169+
['1:2:3', [1, 2, 3]],
170+
];
171+
}
172+
173+
public function dataProviderForSliceToString(): array
174+
{
175+
return [
176+
[':', '::'],
177+
['::', '::'],
178+
['0:', '0::'],
179+
['1:', '1::'],
180+
['-1:', '-1::'],
181+
['0::', '0::'],
182+
['1::', '1::'],
183+
['-1::', '-1::'],
184+
[':0', ':0:'],
185+
[':1', ':1:'],
186+
[':-1', ':-1:'],
187+
[':0:', ':0:'],
188+
[':1:', ':1:'],
189+
[':-1:', ':-1:'],
190+
['0:0:', '0:0:'],
191+
['1:1:', '1:1:'],
192+
['-1:-1:', '-1:-1:'],
193+
['1:1:-1', '1:1:-1'],
194+
['-1:-1:-1', '-1:-1:-1'],
195+
['1:2:3', '1:2:3'],
196+
];
197+
}
198+
199+
public function dataProviderForSliceNormalize(): array
200+
{
201+
return [
202+
[':', 0, '0:0:1', []],
203+
['::', 1, '0:1:1', [0]],
204+
['0:', 2, '0:2:1', [0, 1]],
205+
['1:', 5, '1:5:1', [1, 2, 3, 4]],
206+
['-1:', 3, '2:3:1', [2]],
207+
['0::', 10, '0:10:1', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]],
208+
['1::', 0, '0:0:1', []],
209+
['-1::', 0, '0:0:1', []],
210+
[':0', 1, '0:0:1', []],
211+
[':1', 2, '0:1:1', [0]],
212+
[':-1', 5, '0:4:1', [0, 1, 2, 3]],
213+
[':0:', 3, '0:0:1', []],
214+
[':1:', 1, '0:1:1', [0]],
215+
[':-1:', 3, '0:2:1', [0, 1]],
216+
['0:0:', 3, '0:0:1', []],
217+
['1:1:', 3, '1:1:1', []],
218+
['-1:-1:', 10, '9:9:1', []],
219+
['1:1:-1', 10, '1:1:-1', []],
220+
['-1:-1:-1', 10, '9:9:-1', []],
221+
['1:2:3', 10, '1:2:3', [1]],
222+
['1:2:3', 1, '0:0:3', []],
223+
['::-1', 1, '0:-1:-1', [0]],
224+
['1::-1', 1, '0:-1:-1', [0]],
225+
['2::-1', 1, '0:-1:-1', [0]],
226+
['2:-3:-1', 1, '0:-1:-1', [0]],
227+
['2::-1', 10, '2:-1:-1', [2, 1, 0]],
228+
[':3:-1', 10, '9:3:-1', [9, 8, 7, 6, 5, 4]],
229+
];
230+
}
231+
232+
public function dataProviderForIsSliceArrayTrue(): array
233+
{
234+
return [
235+
[[]],
236+
[[null, null]],
237+
[[null, null, null]],
238+
[[0]],
239+
[[0, null]],
240+
[[0, null, null]],
241+
[[1, null, null]],
242+
[[1, 0, null]],
243+
[[1, 1, null]],
244+
[[-1, 1, null]],
245+
[[1, null, 1]],
246+
[[1, null, 2]],
247+
[[null, null, 1]],
248+
[[null, null, -1]],
249+
[[1, 10, -1]],
250+
];
251+
}
252+
253+
public function dataProviderForIsSliceArrayFalse(): array
254+
{
255+
return [
256+
[['']],
257+
[['a']],
258+
[[0, 1, 'a']],
259+
[[0, 1, 2, 3]],
260+
[[1.1, 1, 2]],
261+
[[1, 1, 2.2]],
262+
[null],
263+
[0],
264+
[1],
265+
[0.0],
266+
[1.0],
267+
[true],
268+
[false],
269+
[new \ArrayObject([])],
270+
[['a' => 1]],
271+
[[[]]],
272+
[[['a' => 1]]],
273+
];
274+
}
275+
}

0 commit comments

Comments
 (0)