<?php
declare(strict_types=1);
namespace Ramsey\Collection\Test;
use Ramsey\Collection\AbstractSet;
use Ramsey\Collection\CollectionInterface;
use Ramsey\Collection\Set;
use Ramsey\Collection\Test\Mock\Foo;
/**
* Tests for Set class.
*
* As Set is a Collection with no duplicated elements
* it only test the expected behavior.
*/
class SetTest extends TestCase
{
/** @var Set<int> */
private Set $set;
protected function setUp(): void
{
parent::setUp();
$this->set = new Set('int');
}
public function testConstructorInheritance(): void
{
$this->assertInstanceOf(CollectionInterface::class, $this->set);
$this->assertInstanceOf(AbstractSet::class, $this->set);
}
public function testConstructGetType(): void
{
$this->assertSame('int', $this->set->getType());
}
public function testConstructWithValues(): void
{
$expected = [2, 4, 6, 8];
$localSet = new Set('int', $expected);
$this->assertSame($expected, $localSet->toArray());
}
public function testAddDuplicates(): void
{
$this->assertTrue($this->set->add(100));
$this->assertFalse($this->set->add(100));
$this->assertSame([100], $this->set->toArray());
}
public function testOffsetSetDuplicates(): void
{
$this->set[] = 100;
$this->set[] = 100;
$this->assertSame([100], $this->set->toArray());
}
public function testUsingEqualButNotIdentical(): void
{
/** @var Set<Foo> $uniqueFoos */
$uniqueFoos = new Set(Foo::class);
// the comparisons are identical (===), not equal(==)
$this->assertTrue($uniqueFoos->add(new Foo()));
$this->assertTrue($uniqueFoos->add(new Foo()));
}
public function testUsingIdentical(): void
{
/** @var Set<Foo> $uniqueFoos */
$uniqueFoos = new Set(Foo::class);
// the comparisons are identical (===), not equal(==)
$identical = new Foo();
$this->assertTrue($uniqueFoos->add($identical));
$this->assertFalse($uniqueFoos->add($identical));
}
public function testMergingSets(): void
{
/** @var Set<string> $set1 */
$set1 = new Set('string', ['X', 'Y']);
/** @var Set<string> $set2 */
$set2 = new Set('string', ['Y', 'Z']);
/** @var Set<string> $set3 */
$set3 = $set1->merge($set2);
$this->assertSame(['X', 'Y', 'Z'], $set3->toArray());
}
public function testMergingSetsOfObjects(): void
{
$obj1 = new Foo();
$obj2 = new Foo();
$obj3 = new Foo();
$set1 = new Set(Foo::class, [$obj1, $obj2]);
$set2 = new Set(Foo::class, [$obj2, $obj3]);
$set3 = $set1->merge($set2);
$this->assertSame([$obj1, $obj2, $obj3], $set3->toArray());
}
/**
* Sets don't normally have keys...
*
* If a set has keys, when attempting to add a value that a set already
* contains, even if the key is different, the new value cannot be added
* because a set cannot contain duplicate values.
*
* In this test, the resulting merged set does not contain the "c" key
* because the set already contains $obj2 when it attempts to add $obj2
* again.
*/
public function testMergingSetsOfObjectsWithKeysAlternate1(): void
{
$obj1 = new Foo();
$obj2 = new Foo();
$obj3 = new Foo();
$set1 = new Set(Foo::class, ['a' => $obj1, 'b' => $obj2]);
$set2 = new Set(Foo::class, ['c' => $obj2, 'd' => $obj3]);
$set3 = $set1->merge($set2);
$this->assertSame(['a' => $obj1, 'b' => $obj2, 'd' => $obj3], $set3->toArray());
}
/**
* Sets don't normally have keys...
*
* According to standard array merging rules, later values for the same
* string keys will overwrite previous ones. This rule is in effect here,
* but with a set, this can also cause loss of data, depending on the order
* of the values being merged.
*
* In this test, $obj2 does not appear in the merged collection because of
* the order in which the merging occurs. First, $obj2 is found at key "b."
* When merging the second set, we see $obj2 at key "c," but we find that
* our set already contains $obj2, so we don't try to add it again. Then, we
* merge $obj3 to the key "b," which overwrites $obj2.
*/
public function testMergingSetsOfObjectsWithKeysAlternate2(): void
{
$obj1 = new Foo();
$obj2 = new Foo();
$obj3 = new Foo();
$set1 = new Set(Foo::class, ['a' => $obj1, 'b' => $obj2]);
$set2 = new Set(Foo::class, ['c' => $obj2, 'b' => $obj3]);
$set3 = $set1->merge($set2);
$this->assertSame(['a' => $obj1, 'b' => $obj3], $set3->toArray());
}
/**
* Sets don't normally have keys...
*
* This test shows how order can affect behavior when merging. It is very
* similar to {@see SetTest::testMergingSetsOfObjectsWithKeysAlternate2()},
* but $set2 now contains keys "b" and "c" in alphabetical order, so when
* merging, $obj3 is stored to key "b" and overwrites $obj2 that was
* previously stored there. When we encounter key "c" (which also has $obj2),
* this value no longer exists in the merged array, so can add it, and there
* won't be a duplicate.
*/
public function testMergingSetsOfObjectsWithKeysAlternate3(): void
{
$obj1 = new Foo();
$obj2 = new Foo();
$obj3 = new Foo();
$set1 = new Set(Foo::class, ['a' => $obj1, 'b' => $obj2]);
$set2 = new Set(Foo::class, ['b' => $obj3, 'c' => $obj2]);
$set3 = $set1->merge($set2);
$this->assertSame(['a' => $obj1, 'b' => $obj3, 'c' => $obj2], $set3->toArray());
}
}