From 23dd4721ece298ba7d2d01ab1ed32c5e6aedcac4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 29 Feb 2016 12:23:08 +0100 Subject: [PATCH] ExtensibleSet: basic implementation & tests --- .../Director/IcingaConfig/ExtensibleSet.php | 199 ++++++++++++++++++ .../IcingaConfig/ExtensibleSetTest.php | 135 ++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 library/Director/IcingaConfig/ExtensibleSet.php create mode 100644 test/php/library/Director/IcingaConfig/ExtensibleSetTest.php diff --git a/library/Director/IcingaConfig/ExtensibleSet.php b/library/Director/IcingaConfig/ExtensibleSet.php new file mode 100644 index 00000000..98bd6b89 --- /dev/null +++ b/library/Director/IcingaConfig/ExtensibleSet.php @@ -0,0 +1,199 @@ +override($values); + } + } + + public function override($values) + { + $this->ownValues = array(); + $this->inheritedValues = array(); + + $this->addValuesTo($this->ownValues, $values); + + return $this->addResolvedValues($values); + } + + public function extend($values) + { + $this->addValuesTo($this->plusValues, $values); + return $this->addResolvedValues($values); + } + + public function blacklist($values) + { + $this->addValuesTo($this->minusValues, $values); + + if ($this->hasBeenResolved()) { + $this->removeValuesFrom($this->resolvedValues, $values); + } + + return $this; + } + + public function getResolvedValues() + { + if (! $this->hasBeenResolved()) { + $this->recalculate(); + } + + sort($this->resolvedValues); + + return $this->resolvedValues; + } + + protected function hasBeenResolved() + { + return $this->resolvedValues !== null; + } + + public function inheritFrom(ExtensibleSet $parent) + { + if ($this->ownValues !== null) { + return $this; + } + + if ($this->hasBeenResolved()) { + $this->reset(); + } + + $this->inheritedValues = array(); + + $this->addValuesTo( + $this->inheritedValues, + $this->stripBlacklistedValues($parent->getResolvedValues()) + ); + + return $this->recalculate(); + } + + protected function stripBlacklistedValues($array) + { + $this->removeValuesFrom($array, $this->minusValues); + + return $array; + } + + public function forgetInheritedValues() + { + $this->inheritedValues = array(); + return $this; + } + + protected function assertValidValue($value) + { + if (null === $this->allowedValues) { + return $this; + } + + if (in_array($value, $this->allowedValues)) { + return $this; + } + + throw new InvalidPropertyException( + 'Got invalid property "%s", allowed are: (%s)', + $value, + implode(', ', $this->allowedValues) + ); + } + + protected function addValuesTo(&$array, $values) + { + foreach ($this->wantArray($values) as $value) { + $this->addTo($array, $value); + } + + return $this; + } + + protected function addResolvedValues($values) + { + if (! $this->hasBeenResolved()) { + $this->resolvedValues = array(); + } + + return $this->addValuesTo( + $this->resolvedValues, + $this->stripBlacklistedValues($this->wantArray($values)) + ); + } + + protected function removeValuesFrom(&$array, $values) + { + foreach ($this->wantArray($values) as $value) { + $this->removeFrom($array, $value); + } + + return $this; + } + + protected function addTo(&$array, $value) + { + if (! in_array($value, $array)) { + $this->assertValidValue($value); + $array[] = $value; + } + + return $this; + } + + protected function removeFrom(&$array, $value) + { + if (false !== ($pos = array_search($value, $array))) { + unset($array[$pos]); + } + + return $this; + } + + protected function recalculate() + { + $this->resolvedValues = array(); + if ($this->ownValues === null) { + $this->addValuesTo($this->resolvedValues, $this->inheritedValues); + } else { + $this->addValuesTo($this->resolvedValues, $this->ownValues); + } + $this->addValuesTo($this->resolvedValues, $this->plusValues); + $this->removeFrom($this->resolvedValues, $this->minusValues); + + return $this; + } + + protected function reset() + { + $this->resolvedValues = null; + + return $this; + } + + protected function wantArray($values) + { + if (is_array($values)) { + return $values; + } + + return array($values); + } +} diff --git a/test/php/library/Director/IcingaConfig/ExtensibleSetTest.php b/test/php/library/Director/IcingaConfig/ExtensibleSetTest.php new file mode 100644 index 00000000..f1fa189b --- /dev/null +++ b/test/php/library/Director/IcingaConfig/ExtensibleSetTest.php @@ -0,0 +1,135 @@ +assertEquals( + array(), + $set->getResolvedValues() + ); + } + + public function testValuesPassedToConstructorAreAccepted() + { + $values = array('Val1', 'Val2', 'Val4'); + $set = new ExtensibleSet($values); + + $this->assertEquals( + $values, + $set->getResolvedValues() + ); + } + + public function testSingleValuesCanBeBlacklisted() + { + $values = array('Val1', 'Val2', 'Val4'); + $set = new ExtensibleSet($values); + $set->blacklist('Val2'); + + $this->assertEquals( + array('Val1', 'Val4'), + $set->getResolvedValues() + ); + } + + public function testMultipleValuesCanBeBlacklisted() + { + $values = array('Val1', 'Val2', 'Val4'); + $set = new ExtensibleSet($values); + $set->blacklist(array('Val4', 'Val1')); + + $this->assertEquals( + array('Val2'), + $set->getResolvedValues() + ); + } + + public function testSimpleInheritanceWorksFine() + { + $values = array('Val1', 'Val2', 'Val4'); + $parent = new ExtensibleSet($values); + $child = new ExtensibleSet(); + $child->inheritFrom($parent); + + $this->assertEquals( + $values, + $child->getResolvedValues() + ); + } + + public function testWeCanInheritFromMultipleParents() + { + $p1set = array('p1a', 'p1c'); + $p2set = array('p2a', 'p2d'); + $parent1 = new ExtensibleSet($p1set); + $parent2 = new ExtensibleSet($p2set); + $child = new ExtensibleSet(); + $child->inheritFrom($parent1)->inheritFrom($parent2); + + $this->assertEquals( + $p2set, + $child->getResolvedValues() + ); + } + + public function testOwnValuesOverrideParents() + { + $cset = array('p1a', 'p1c'); + $pset = array('p2a', 'p2d'); + $child = new ExtensibleSet($cset); + $parent = new ExtensibleSet($pset); + $child->inheritFrom($parent); + + $this->assertEquals( + $cset, + $child->getResolvedValues() + ); + } + + public function testInheritedValuesCanBeBlacklisted() + { + $pset = array('p1', 'p2', 'p3'); + + $child = new ExtensibleSet(); + $child->blacklist('p2'); + + $parent = new ExtensibleSet($pset); + $child->inheritFrom($parent); + $child->blacklist(array('not', 'yet', 'p1')); + + $this->assertEquals( + array('p3'), + $child->getResolvedValues() + ); + + $child->blacklist(array('p3')); + $this->assertEquals( + array(), + $child->getResolvedValues() + ); + } + + public function testInheritedValuesCanBeExtended() + { + $pset = array('p1', 'p2', 'p3'); + + $child = new ExtensibleSet(); + $child->extend('p5'); + + $parent = new ExtensibleSet($pset); + $child->inheritFrom($parent); + + $this->assertEquals( + array('p1', 'p2', 'p3', 'p5'), + $child->getResolvedValues() + ); + } +}