diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f41572..ac2a92f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ dev - Add `Providers\DSL\Compilation\Parameters\ParameterCollection::remove` to remove a previously added parameter.\ - Introduce `Analysis\INativeType::TYPE_NUMERIC` acting as a union type for `Analysis\INativeType::TYPE_INT` and `Analysis\INativeType::TYPE_DOUBLE` - Fix bug when joining to the same `ITraversable` instance with an `->indexBy(...)` only returning the first element. + - Change identity hash to hash instances of `DateTimeInterface` by class and timestamp hence treating it as a value type throughout the PINQ API 3.1.0 (29/3/15) =============== diff --git a/Source/Iterators/Common/Identity.php b/Source/Iterators/Common/Identity.php index 5d10389..7c2148e 100644 --- a/Source/Iterators/Common/Identity.php +++ b/Source/Iterators/Common/Identity.php @@ -45,7 +45,9 @@ public static function hash($value) case 'o': //object - return 'o' . spl_object_hash($value); + return $value instanceof \DateTimeInterface || $value instanceof \DateTime + ? 'dt:' . get_class($value) . ':' . $value->getTimestamp() + : 'o' . spl_object_hash($value); case 'a': //array diff --git a/Tests/Helpers/ExtendedDateTime.php b/Tests/Helpers/ExtendedDateTime.php new file mode 100644 index 0000000..6d4ac4a --- /dev/null +++ b/Tests/Helpers/ExtendedDateTime.php @@ -0,0 +1,12 @@ + + */ +class ExtendedDateTime extends \DateTime +{ + +} \ No newline at end of file diff --git a/Tests/Integration/Collection/GroupJoinApplyTest.php b/Tests/Integration/Collection/GroupJoinApplyTest.php index b1cd904..7cb5f6f 100644 --- a/Tests/Integration/Collection/GroupJoinApplyTest.php +++ b/Tests/Integration/Collection/GroupJoinApplyTest.php @@ -149,6 +149,36 @@ public function testThatApplyGroupJoinWithDefaultValueOperatedCorrectly(\Pinq\IC '10:1,3,5,7,9,11,13,15,17,19', ]); } + + /** + * @dataProvider oneToTen + */ + public function testThatOnEqualityWillNotMatchNullsAndUseDefault(\Pinq\ICollection $collection, array $data) + { + $collection + ->groupJoin($collection) + ->onEquality( + function ($i) { return $i % 2 === 0 ? $i : null; }, + function ($i) { return $i % 2 === 0 ? $i : null; }) + ->withDefault('') + ->apply(function (&$outer, \Pinq\ITraversable $innerGroup) { + $outer .= ':' . $innerGroup->implode('-'); + }); + + $this->assertMatches($collection, [ + '1:', + '2:2', + '3:', + '4:4', + '5:', + '6:6', + '7:', + '8:8', + '9:', + '10:10' + ]); + } + /** * @dataProvider oneToTen */ diff --git a/Tests/Integration/Collection/JoinApplyTest.php b/Tests/Integration/Collection/JoinApplyTest.php index 1ace1e4..5f4f5cc 100644 --- a/Tests/Integration/Collection/JoinApplyTest.php +++ b/Tests/Integration/Collection/JoinApplyTest.php @@ -206,31 +206,25 @@ function ($i) { return $i % 2 === 0 ? $i : null; }) } /** - * @dataProvider oneToTen + * @dataProvider emptyData */ - public function testThatOnEqualityWillNotMatchNullsAndUseDefault(\Pinq\ICollection $collection, array $data) + public function testThatOnEqualityWillPassForValueWiseIdenticalDateTimes(\Pinq\ICollection $collection, array $data) { + $dateTime = new \DateTime('2-1-2001'); + $anotherDateTime = new \DateTime('2-1-2000'); + $collection[] = $dateTime; + $collection[] = $anotherDateTime; + $collection - ->groupJoin($collection) - ->onEquality( - function ($i) { return $i % 2 === 0 ? $i : null; }, - function ($i) { return $i % 2 === 0 ? $i : null; }) - ->withDefault('') - ->apply(function (&$outer, \Pinq\ITraversable $innerGroup) { - $outer .= ':' . $innerGroup->implode('-'); + ->join([$dateTime]) + ->onEquality(function ($outer) { return $outer; }, function ($inner) { return $inner; }) + ->apply(function (&$outer) { + $outer = ''; }); - $this->assertMatches($collection, [ - '1:', - '2:2', - '3:', - '4:4', - '5:', - '6:6', - '7:', - '8:8', - '9:', - '10:10' + $this->assertMatchesValues($collection, [ + '', + $anotherDateTime ]); } } diff --git a/Tests/Integration/Collection/RemoveTest.php b/Tests/Integration/Collection/RemoveTest.php index ee7d21c..0e01752 100644 --- a/Tests/Integration/Collection/RemoveTest.php +++ b/Tests/Integration/Collection/RemoveTest.php @@ -33,4 +33,20 @@ public function testThatRemoveWillRemovesIdenticalValuesFromCollectionAndPreserv $this->assertMatchesValues($collection, $data); } + + /** + * @dataProvider dateTimes + */ + public function testRemovesDateTimeClassesByValueRatherThenReference( + \Pinq\ICollection $collection, + array $data + ) { + $originalCount = $collection->count(); + $dateTime = $collection[2]; + + $collection->remove($dateTime); + + $this->assertCount($originalCount - 1, $collection); + $this->assertNotContains($dateTime, $collection); + } } diff --git a/Tests/Integration/Collection/SetIndexTest.php b/Tests/Integration/Collection/SetIndexTest.php index 07d8b54..38f50dd 100644 --- a/Tests/Integration/Collection/SetIndexTest.php +++ b/Tests/Integration/Collection/SetIndexTest.php @@ -24,7 +24,7 @@ public function testThatSettingAnIndexWillOverrideTheElementInTheCollection(\Pin } /** - * @dataProvider everything + * @dataProvider emptyData */ public function testThatSetIndexWithNoKeyAppendsTheValueWithTheNextLargestIntGreaterThanOrEqualToZeroLikeAnArray(\Pinq\ICollection $collection, array $data) { @@ -53,4 +53,21 @@ public function testThatSetIndexWithNoKeyAppendsTheValueWithTheNextLargestIntGre $this->assertSame('boo', $collection[1]); } + + /** + * @dataProvider emptyData + */ + public function testThatDateTimeIndexesAreComparedByValue(\Pinq\ICollection $collection, array $data) + { + $dateTime = new \DateTime('2-1-2001'); + $anotherDateTime = new \DateTime('2-1-2000'); + + $collection[$dateTime] = 1; + $collection[$anotherDateTime] = 2; + $collection[clone $anotherDateTime] = 3; + + $this->assertSame(1, $collection[$dateTime]); + $this->assertSame(3, $collection[$anotherDateTime]); + $this->assertSame(3, $collection[clone $anotherDateTime]); + } } diff --git a/Tests/Integration/Collection/UnsetIndexTest.php b/Tests/Integration/Collection/UnsetIndexTest.php index 5f13168..1ea014d 100644 --- a/Tests/Integration/Collection/UnsetIndexTest.php +++ b/Tests/Integration/Collection/UnsetIndexTest.php @@ -20,4 +20,22 @@ public function testThatUnsetingAnIndexWillRemoveTheElementFromTheCollection(\Pi $this->assertMatches($collection, $data); } + + /** + * @dataProvider emptyData + */ + public function testThatDateTimeIndexesAreComparedByValue(\Pinq\ICollection $collection, array $data) + { + $dateTime = new \DateTime('2-1-2001'); + $anotherDateTime = new \DateTime('2-1-2000'); + + $collection[$dateTime] = 1; + $collection[$anotherDateTime] = 2; + unset($collection[new \DateTime('2-1-2000')]); + + $this->assertTrue(isset($collection[$dateTime])); + $this->assertFalse(isset($collection[$anotherDateTime])); + $this->assertFalse(isset($collection[clone $anotherDateTime])); + $this->assertFalse(isset($collection[new \DateTime('2-1-2000')])); + } } diff --git a/Tests/Integration/DataTest.php b/Tests/Integration/DataTest.php index a75af2b..2b2fbe0 100644 --- a/Tests/Integration/DataTest.php +++ b/Tests/Integration/DataTest.php @@ -28,7 +28,8 @@ public function everything() 'oneToTenTwice', 'assocOneToTen', 'tenRandomStrings', - 'assocMixedValues' + 'assocMixedValues', + 'dateTimes' ]; foreach ($dataProviders as $provider) { @@ -106,6 +107,17 @@ public function assocTenRandomStrings() return $this->getImplementations(array_combine($this->randomStrings(10), $this->randomStrings(10))); } + public function dateTimes() + { + return $this->getImplementations(iterator_to_array( + new \DatePeriod( + new \DateTime('1-1-2000'), + new \DateInterval('P4M'), + new \DateTime('2-3-2004') + ) + )); + } + private function randomStrings($amount) { $letters = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890-!@#$%^&*()_'; diff --git a/Tests/Integration/Traversable/GetIndexTest.php b/Tests/Integration/Traversable/GetIndexTest.php index 26a09ae..434d4e1 100644 --- a/Tests/Integration/Traversable/GetIndexTest.php +++ b/Tests/Integration/Traversable/GetIndexTest.php @@ -61,4 +61,26 @@ public function testThatIndexesSupportArrayKeys(\Pinq\ITraversable $traversable, 'Should be using strict equality for arrays (order matters)'); } } + + /** + * @dataProvider emptyData + */ + public function testThatDateTimeIndexesAreComparedByValueAndClass(\Pinq\ITraversable $traversable, array $data) + { + $dateTime = new \DateTime('2-1-2001'); + $anotherDateTime = new \DateTime('2-1-2000'); + + $traversable = $traversable + ->append([$dateTime, $anotherDateTime]) + ->indexBy(function ($value) { return $value; }) + ->select(function (\DateTime $dateTime) { return $dateTime->format('j-n-Y'); }); + + $this->assertSame('2-1-2001', $traversable[$dateTime]); + $this->assertSame('2-1-2001', $traversable[clone $dateTime]); + $this->assertSame('2-1-2001', $traversable[new \DateTime('2-1-2001')]); + + if(class_exists('DateTimeImmutable', false)) { + $this->assertNull($traversable[new \DateTimeImmutable('2-1-2001')]); + } + } } diff --git a/Tests/Integration/Traversable/GroupByTest.php b/Tests/Integration/Traversable/GroupByTest.php index 6b3ad8d..2e48d88 100644 --- a/Tests/Integration/Traversable/GroupByTest.php +++ b/Tests/Integration/Traversable/GroupByTest.php @@ -2,6 +2,8 @@ namespace Pinq\Tests\Integration\Traversable; +use Pinq\ITraversable; + class GroupByTest extends TraversableTest { protected function _testReturnsNewInstanceOfSameTypeWithSameScheme(\Pinq\ITraversable $traversable) @@ -137,4 +139,23 @@ public function testThatGroupByMaintainsArrayReferences(\Pinq\ITraversable $trav $this->assertSame($data, [[1], [2], [1, 2, 'foo' => 1], [3, 5, 'foo' => 3], [4, 2, 'foo' => 4]]); } + + /** + * @dataProvider emptyData + */ + public function testThatDateTimeAreGroupedByValue(\Pinq\ITraversable $traversable, array $data) + { + $traversable = $traversable + ->append([2000, 2000, 2001, 2000, 2002, 2000, 2001, 2000, 2002]) + ->groupBy(function ($year) { return new \DateTime('1-1-' . $year); }) + ->select(function (ITraversable $years, \DateTime $dateTime) { + return [$dateTime->format('Y'), $years->count()]; + }); + + $this->assertMatchesValues($traversable, [ + ['2000', 5], + ['2001', 2], + ['2002', 2], + ]); + } } diff --git a/Tests/Integration/Traversable/IssetIndexTest.php b/Tests/Integration/Traversable/IssetIndexTest.php index 6eb3be2..cdcb40d 100644 --- a/Tests/Integration/Traversable/IssetIndexTest.php +++ b/Tests/Integration/Traversable/IssetIndexTest.php @@ -2,6 +2,8 @@ namespace Pinq\Tests\Integration\Traversable; +use Pinq\Tests\Helpers\ExtendedDateTime; + class IssetIndexTest extends TraversableTest { /** @@ -27,4 +29,22 @@ public function testThatIssetOnInvalidIndexesReturnFalse(\Pinq\ITraversable $tra $this->assertFalse(isset($traversable[$notAnIndex])); } + + /** + * @dataProvider emptyData + */ + public function testThatDateTimeIndexesAreComparedByValueAndClass(\Pinq\ITraversable $traversable, array $data) + { + $dateTime = new \DateTime('2-1-2001'); + $anotherDateTime = new \DateTime('2-1-2000'); + + $traversable = $traversable + ->append([$dateTime, $anotherDateTime]) + ->indexBy(function ($value) { return $value; }); + + $this->assertTrue(isset($traversable[$dateTime])); + $this->assertTrue(isset($traversable[clone $dateTime])); + $this->assertTrue(isset($traversable[new \DateTime('2-1-2001')])); + $this->assertFalse(isset($traversable[new ExtendedDateTime('2-1-2001')])); + } } diff --git a/Tests/Integration/Traversable/JoinTest.php b/Tests/Integration/Traversable/JoinTest.php index 55a6719..2e2e51c 100644 --- a/Tests/Integration/Traversable/JoinTest.php +++ b/Tests/Integration/Traversable/JoinTest.php @@ -374,4 +374,25 @@ public function testJoinToSelfWithInnerIndexBy(\Pinq\ITraversable $traversable) '4:3', ]); } + + /** + * @dataProvider emptyData + */ + public function testThatOnEqualityWillPassForValueWiseIdenticalDateTimes(\Pinq\ITraversable $traversable, array $data) + { + $dateTime = new \DateTime('2-1-2001'); + $anotherDateTime = new \DateTime('2-1-2000'); + + $traversable = $traversable + ->append([$dateTime, $anotherDateTime]) + ->join([$dateTime]) + ->onEquality(function ($outer) { return $outer; }, function ($inner) { return $inner; }) + ->to(function (\DateTime $outer) { + return $outer->format('d-m-Y'); + }); + + $this->assertMatchesValues($traversable, [ + '02-01-2001', + ]); + } } diff --git a/Tests/Integration/Traversable/UnionTest.php b/Tests/Integration/Traversable/UnionTest.php index 09ed380..06e1407 100644 --- a/Tests/Integration/Traversable/UnionTest.php +++ b/Tests/Integration/Traversable/UnionTest.php @@ -2,6 +2,8 @@ namespace Pinq\Tests\Integration\Traversable; +use Pinq\Tests\Helpers\ExtendedDateTime; + class UnionTest extends TraversableTest { protected function _testReturnsNewInstanceOfSameTypeWithSameScheme(\Pinq\ITraversable $traversable) @@ -63,4 +65,40 @@ public function testThatUnionMaintainsReferences(\Pinq\ITraversable $traversable $this->assertSame('a-b-c-d-e-f-', implode('', $data)); } + + /** + * @dataProvider emptyData + */ + public function testThatUnionWillMatchDateTimeByTimestampAndClass(\Pinq\ITraversable $traversable, array $data) + { + $traversable = $traversable + ->append([ + new \DateTime('2-1-2001'), + new \DateTime('2-1-2001'), + new \DateTime('2-1-2001 00:00:01'), + new \DateTime('1-1-2001'), + new \DateTime('1-1-2001'), + new \DateTime('3-1-2001'), + new ExtendedDateTime('1-1-2001'), + ]) + ->union([ + new \DateTime('4-1-2001'), + new \DateTime('2-1-2001'), + new ExtendedDateTime('1-1-2001'), + new ExtendedDateTime('2-1-2001'), + ]) + ->select(function (\DateTime $outer) { + return get_class($outer) . ':' . $outer->format('d-m-Y H:i:s'); + }); + + $this->assertMatchesValues($traversable, [ + 'DateTime:02-01-2001 00:00:00', + 'DateTime:02-01-2001 00:00:01', + 'DateTime:01-01-2001 00:00:00', + 'DateTime:03-01-2001 00:00:00', + 'Pinq\Tests\Helpers\ExtendedDateTime:01-01-2001 00:00:00', + 'DateTime:04-01-2001 00:00:00', + 'Pinq\Tests\Helpers\ExtendedDateTime:02-01-2001 00:00:00', + ]); + } } diff --git a/Tests/Integration/Traversable/UniqueTest.php b/Tests/Integration/Traversable/UniqueTest.php index f3dbec7..420bb98 100644 --- a/Tests/Integration/Traversable/UniqueTest.php +++ b/Tests/Integration/Traversable/UniqueTest.php @@ -2,6 +2,9 @@ namespace Pinq\Tests\Integration\Traversable; +use Pinq\Analysis\TypeData\DateTime; +use Pinq\Tests\Helpers\ExtendedDateTime; + class UniqueTest extends TraversableTest { protected function _testReturnsNewInstanceOfSameTypeWithSameScheme(\Pinq\ITraversable $traversable) @@ -107,4 +110,33 @@ public function testThatUniqueMaintainsReferences(\Pinq\ITraversable $traversabl $this->assertSame([10, 30, 50, 70, 90, 1, 20, 3, 40, 5, 60, 7, 80, 9, 100], $data); } + + /** + * @dataProvider emptyData + */ + public function testThatUniqueWillDateTimeByTimestampAndClass(\Pinq\ITraversable $traversable, array $data) + { + $traversable = $traversable + ->append([ + new \DateTime('2-1-2001'), + new \DateTime('2-1-2001'), + new \DateTime('2-1-2001 00:00:01'), + new \DateTime('1-1-2001'), + new \DateTime('1-1-2001'), + new \DateTime('3-1-2001'), + new ExtendedDateTime('1-1-2001'), + ]) + ->unique() + ->select(function (\DateTime $outer) { + return get_class($outer) . ':' . $outer->format('d-m-Y H:i:s'); + }); + + $this->assertMatchesValues($traversable, [ + 'DateTime:02-01-2001 00:00:00', + 'DateTime:02-01-2001 00:00:01', + 'DateTime:01-01-2001 00:00:00', + 'DateTime:03-01-2001 00:00:00', + 'Pinq\Tests\Helpers\ExtendedDateTime:01-01-2001 00:00:00', + ]); + } }