From 8a57ca7850ec644bbdd5bcd7552d6101965c072b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 17 Jan 2023 17:44:08 +0100 Subject: [PATCH 1/3] Introduce monitoring's perfdata tests here --- test/php/Lib/PerfdataSetWithPublicData.php | 12 + .../library/Icingadb/Util/PerfdataSetTest.php | 120 ++++++ .../library/Icingadb/Util/PerfdataTest.php | 402 ++++++++++++++++++ .../Icingadb/Util/ThresholdRangeTest.php | 323 ++++++++++++++ 4 files changed, 857 insertions(+) create mode 100644 test/php/Lib/PerfdataSetWithPublicData.php create mode 100644 test/php/library/Icingadb/Util/PerfdataSetTest.php create mode 100644 test/php/library/Icingadb/Util/PerfdataTest.php create mode 100644 test/php/library/Icingadb/Util/ThresholdRangeTest.php diff --git a/test/php/Lib/PerfdataSetWithPublicData.php b/test/php/Lib/PerfdataSetWithPublicData.php new file mode 100644 index 000000000..97fcd7abc --- /dev/null +++ b/test/php/Lib/PerfdataSetWithPublicData.php @@ -0,0 +1,12 @@ +assertSame( + 'key1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse valid simple labels' + ); + $this->assertSame( + 'key2', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse valid simple labels' + ); + $this->assertSame( + 'key3', + $pset->perfdata[2]->getLabel(), + 'PerfdataSet does not correctly parse valid simple labels' + ); + } + + public function testWhetherNonQuotedPerfdataLablesWithSpacesAreProperlyParsed() + { + $pset = PerfdataSetWithPublicData::fromString('key 1=val1 key 1 + 1=val2'); + $this->assertSame( + 'key 1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse non quoted labels with spaces' + ); + $this->assertSame( + 'key 1 + 1', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse non quoted labels with spaces' + ); + } + + public function testWhetherValidQuotedPerfdataLabelsAreProperlyParsed() + { + $pset = PerfdataSetWithPublicData::fromString('\'key 1\'=val1 "key 2"=val2 \'a=b\'=0%;;2'); + $this->assertSame( + 'key 1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse valid quoted labels' + ); + $this->assertSame( + 'key 2', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse valid quoted labels' + ); + $this->assertSame( + 'a=b', + $pset->perfdata[2]->getLabel(), + 'PerfdataSet does not correctly parse labels with equal signs' + ); + } + + public function testWhetherInvalidQuotedPerfdataLabelsAreProperlyParsed() + { + $pset = PerfdataSetWithPublicData::fromString('\'key 1=1 key 2"=2'); + $this->assertSame( + 'key 1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse invalid quoted labels' + ); + $this->assertSame( + 'key 2"', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse invalid quoted labels' + ); + $pset = PerfdataSetWithPublicData::fromString('"key 1=1 "key 2"=2'); + $this->assertSame( + 'key 1=1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse invalid quoted labels' + ); + $this->assertNull( + $pset->perfdata[0]->getValue() + ); + $this->assertSame( + '2"', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse invalid quoted labels' + ); + $this->assertSame( + '2', + $pset->perfdata[1]->getValue() + ); + } + + /** + * @depends testWhetherValidSimplePerfdataLabelsAreProperlyParsed + */ + public function testWhetherAPerfdataSetIsIterable() + { + $pset = PerfdataSet::fromString('key=value'); + foreach ($pset as $p) { + $this->assertSame('key', $p->getLabel()); + return; + } + + $this->fail('PerfdataSet objects cannot be iterated'); + } + + public function testWhetherPerfdataSetsCanBeInitializedWithEmptyStrings() + { + $pset = PerfdataSetWithPublicData::fromString(''); + $this->assertEmpty($pset->perfdata, 'PerfdataSet::fromString does not accept emtpy strings'); + } +} diff --git a/test/php/library/Icingadb/Util/PerfdataTest.php b/test/php/library/Icingadb/Util/PerfdataTest.php new file mode 100644 index 000000000..e31c86d00 --- /dev/null +++ b/test/php/library/Icingadb/Util/PerfdataTest.php @@ -0,0 +1,402 @@ +expectException(\InvalidArgumentException::class); + + Perfdata::fromString(''); + } + + public function testWhetherFromStringThrowsExceptionWhenGivenAnInvalidString() + { + $this->expectException(\InvalidArgumentException::class); + + Perfdata::fromString('test'); + } + + public function testWhetherFromStringParsesAGivenStringCorrectly() + { + $p = Perfdata::fromString('key=1234'); + $this->assertSame( + 'key', + $p->getLabel(), + 'Perfdata::fromString does not properly parse performance data labels' + ); + $this->assertSame( + '1234', + $p->getValue(), + 'Perfdata::fromString does not properly parse performance data values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetValueReturnsValidValues() + { + $this->assertSame( + '1337', + Perfdata::fromString('test=1337')->getValue(), + 'Perfdata::getValue does not return correct values' + ); + $this->assertSame( + '1337', + Perfdata::fromString('test=1337;;;;')->getValue(), + 'Perfdata::getValue does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherDecimalValuesAreCorrectlyParsed() + { + $this->assertSame( + '1337.5', + Perfdata::fromString('test=1337.5')->getValue(), + 'Perfdata objects do not parse decimal values correctly' + ); + $this->assertSame( + '1337.5', + Perfdata::fromString('test=1337.5B')->getValue(), + 'Perfdata objects do not parse decimal values correctly' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetValueReturnsNullForInvalidOrUnknownValues() + { + $this->assertNull( + Perfdata::fromString('test=U')->getValue(), + 'Perfdata::getValue does not return null for unknown values' + ); + $this->assertNull( + Perfdata::fromString('test=i am not a value')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=-kW')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=kW')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=-')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherUnitOfUnkownValuesIsCorrectlyIdentified() + { + $this->assertNull( + Perfdata::fromString('test=U')->getUnit(), + 'Perfdata::getUnit does not return null for unknown values' + ); + $this->assertNull( + Perfdata::fromString('test=i am not a value')->getUnit(), + 'Perfdata::getUnit does not return null for unknown values' + ); + $this->assertNull( + PerfData::fromString('test=')->getUnit(), + 'Perfdata::getUnit does not return null for unknown values' + ); + $this->assertSame( + 'kW', + PerfData::fromString('test=-kW')->getUnit(), + 'Perfdata::getUnit does not return correct unit for invalid values' + ); + $this->assertSame( + 'kW', + PerfData::fromString('test=kW')->getUnit(), + 'Perfdata::getUnit does not return correct unit for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=-')->getUnit(), + 'Perfdata::getUnit does not return null for unknown values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhethergetWarningThresholdReturnsCorrectValues() + { + $zeroToTen = Perfdata::fromString('test=1;10')->getWarningThreshold(); + $this->assertSame( + 0.0, + $zeroToTen->getMin(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $this->assertSame( + 10.0, + $zeroToTen->getMax(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $tenToInfinity = Perfdata::fromString('test=1;10:')->getWarningThreshold(); + $this->assertSame( + 10.0, + $tenToInfinity->getMin(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $this->assertNull( + $tenToInfinity->getMax(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $infinityToTen = Perfdata::fromString('test=1;~:10')->getWarningThreshold(); + $this->assertNull( + $infinityToTen->getMin(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $this->assertSame( + 10.0, + $infinityToTen->getMax(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $tenToTwenty = Perfdata::fromString('test=1;10:20')->getWarningThreshold(); + $this->assertSame( + 10.0, + $tenToTwenty->getMin(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $this->assertSame( + 20.0, + $tenToTwenty->getMax(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $tenToTwentyInverted = Perfdata::fromString('test=1;@10:20')->getWarningThreshold(); + $this->assertTrue( + $tenToTwentyInverted->isInverted(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetCriticalThresholdReturnsCorrectValues() + { + $zeroToTen = Perfdata::fromString('test=1;;10')->getCriticalThreshold(); + $this->assertSame( + 0.0, + $zeroToTen->getMin(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $this->assertSame( + 10.0, + $zeroToTen->getMax(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $tenToInfinity = Perfdata::fromString('test=1;;10:')->getCriticalThreshold(); + $this->assertSame( + 10.0, + $tenToInfinity->getMin(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $this->assertNull( + $tenToInfinity->getMax(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $infinityToTen = Perfdata::fromString('test=1;;~:10')->getCriticalThreshold(); + $this->assertNull( + $infinityToTen->getMin(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $this->assertSame( + 10.0, + $infinityToTen->getMax(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $tenToTwenty = Perfdata::fromString('test=1;;10:20')->getCriticalThreshold(); + $this->assertSame( + 10.0, + $tenToTwenty->getMin(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $this->assertSame( + 20.0, + $tenToTwenty->getMax(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $tenToTwentyInverted = Perfdata::fromString('test=1;;@10:20')->getCriticalThreshold(); + $this->assertTrue( + $tenToTwentyInverted->isInverted(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetMinimumValueReturnsCorrectValues() + { + $this->assertSame( + '1337', + Perfdata::fromString('test=1;;;1337')->getMinimumValue(), + 'Perfdata::getMinimumValue does not return correct values' + ); + $this->assertSame( + '1337.5', + Perfdata::fromString('test=1;;;1337.5')->getMinimumValue(), + 'Perfdata::getMinimumValue does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetMaximumValueReturnsCorrectValues() + { + $this->assertSame( + '1337', + Perfdata::fromString('test=1;;;;1337')->getMaximumValue(), + 'Perfdata::getMaximumValue does not return correct values' + ); + $this->assertSame( + '1337.5', + Perfdata::fromString('test=1;;;;1337.5')->getMaximumValue(), + 'Perfdata::getMaximumValue does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherMissingValuesAreProperlyHandled() + { + $perfdata = Perfdata::fromString('test=1;;3;5'); + $this->assertEmpty( + (string) $perfdata->getWarningThreshold(), + 'Perfdata objects do not correctly identify omitted warning tresholds' + ); + $this->assertNull( + $perfdata->getMaximumValue(), + 'Perfdata objects do not return null for missing maximum values' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsNumber() + { + $this->assertTrue( + Perfdata::fromString('test=666')->isNumber(), + 'Perfdata objects do not identify ordinary digits as number' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsSeconds() + { + $this->assertTrue( + Perfdata::fromString('test=666s')->isSeconds(), + 'Perfdata objects do not identify seconds as seconds' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsPercentage() + { + $this->assertTrue( + Perfdata::fromString('test=66%')->isPercentage(), + 'Perfdata objects do not identify percentages as percentages' + ); + } + + /** + * @depends testWhetherValuesAreIdentifiedAsPercentage + */ + public function testWhetherMinAndMaxAreNotRequiredIfUnitIsInPercent() + { + $perfdata = Perfdata::fromString('test=1%'); + $this->assertSame( + 0.0, + $perfdata->getMinimumValue(), + 'Perfdata objects do not set minimum value to 0 if UOM is %' + ); + $this->assertSame( + 100.0, + $perfdata->getMaximumValue(), + 'Perfdata objects do not set maximum value to 100 if UOM is %' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsBytes() + { + $this->assertTrue( + Perfdata::fromString('test=66666B')->isBytes(), + 'Perfdata objects do not identify bytes as bytes' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsCounter() + { + $this->assertTrue( + Perfdata::fromString('test=123c')->isCounter(), + 'Perfdata objects do not identify counters as counters' + ); + } + + /** + * @depends testWhetherValuesAreIdentifiedAsPercentage + */ + public function testWhetherPercentagesAreHandledCorrectly() + { + $this->assertSame( + '66', + Perfdata::fromString('test=66%')->getPercentage(), + 'Perfdata objects do not correctly handle native percentages' + ); + $this->assertSame( + 50.0, + Perfdata::fromString('test=0;;;-250;250')->getPercentage(), + 'Perfdata objects do not correctly convert suitable values to percentages' + ); + $this->assertNull( + Perfdata::fromString('test=50')->getPercentage(), + 'Perfdata objects do return a percentage though their unit is not % and no maximum is given' + ); + $this->assertNull( + Perfdata::fromString('test=25;;;50;100')->getPercentage(), + 'Perfdata objects do return a percentage though their value is lower than it\'s allowed minimum' + ); + $this->assertNull( + Perfdata::fromString('test=25;;;0;')->getPercentage(), + 'Perfdata objects do not ignore empty max values when returning percentages' + ); + $this->assertNull( + Perfdata::fromString('test=25;;;0;0')->getPercentage(), + 'Perfdata objects do not ignore impossible min/max combinations when returning percentages' + ); + } +} diff --git a/test/php/library/Icingadb/Util/ThresholdRangeTest.php b/test/php/library/Icingadb/Util/ThresholdRangeTest.php new file mode 100644 index 000000000..c0d757371 --- /dev/null +++ b/test/php/library/Icingadb/Util/ThresholdRangeTest.php @@ -0,0 +1,323 @@ +assertSame( + 0.0, + $outside0And10->getMin(), + 'ThresholdRange::fromString() does not identify zero as default minimum for double exclusive ranges' + ); + $this->assertSame( + 10.0, + $outside0And10->getMax(), + 'ThresholdRange::fromString() does not identify ten as explicit maximum for double exclusive ranges' + ); + $this->assertFalse( + $outside0And10->isInverted(), + 'ThresholdRange::fromString() identifies double exclusive ranges as inclusive' + ); + + $outside10And20 = ThresholdRange::fromString('10:20'); + $this->assertSame( + 10.0, + $outside10And20->getMin(), + 'ThresholdRange::fromString() does not identify ten as explicit minimum for double exclusive ranges' + ); + $this->assertSame( + 20.0, + $outside10And20->getMax(), + 'ThresholdRange::fromString() does not identify twenty as explicit maximum for double exclusive ranges' + ); + $this->assertFalse( + $outside10And20->isInverted(), + 'ThresholdRange::fromString() identifies double exclusive ranges as inclusive' + ); + } + + /** + * @depends testFromStringProperlyParsesDoubleExclusiveRanges + */ + public function testContainsCorrectlyEvaluatesDoubleExclusiveRanges() + { + $outside0And10 = ThresholdRange::fromString('10'); + $this->assertFalse( + $outside0And10->contains(-1), + 'ThresholdRange::contains() identifies negative values as greater than or equal to zero' + ); + $this->assertFalse( + $outside0And10->contains(11), + 'ThresholdRange::contains() identifies eleven as smaller than or equal to ten' + ); + $this->assertTrue( + $outside0And10->contains(10), + 'ThresholdRange::contains() identifies 10 as outside the range 0..10' + ); + + $outside10And20 = ThresholdRange::fromString('10:20'); + $this->assertFalse( + $outside10And20->contains(9), + 'ThresholdRange::contains() identifies nine as greater than or equal to 10' + ); + $this->assertFalse( + $outside10And20->contains(21), + 'ThresholdRange::contains() identifies twenty-one as smaller than or equal to twenty' + ); + $this->assertTrue( + $outside10And20->contains(20), + 'ThresholdRange::contains() identifies 20 as outside the range 10..20' + ); + } + + public function testFromStringProperlyParsesSingleExclusiveRanges() + { + $smallerThan10 = ThresholdRange::fromString('10:'); + $this->assertSame( + 10.0, + $smallerThan10->getMin(), + 'ThresholdRange::fromString() does not identify ten as explicit minimum for single exclusive ranges' + ); + $this->assertNull( + $smallerThan10->getMax(), + 'ThresholdRange::fromString() does not identify infinity as default maximum for single exclusive ranges' + ); + $this->assertFalse( + $smallerThan10->isInverted(), + 'ThresholdRange::fromString() identifies single exclusive ranges as inclusive' + ); + + $greaterThan10 = ThresholdRange::fromString('~:10'); + $this->assertNull( + $greaterThan10->getMin(), + 'ThresholdRange::fromString() does not identify infinity as explicit minimum for single exclusive ranges' + ); + $this->assertSame( + 10.0, + $greaterThan10->getMax(), + 'ThresholdRange::fromString() does not identify ten as explicit maximum for single exclusive ranges' + ); + $this->assertFalse( + $greaterThan10->isInverted(), + 'ThresholdRange::fromString() identifies single exclusive ranges as inclusive' + ); + } + + /** + * @depends testFromStringProperlyParsesSingleExclusiveRanges + */ + public function testContainsCorrectlyEvaluatesSingleExclusiveRanges() + { + $smallerThan10 = ThresholdRange::fromString('10:'); + $this->assertFalse( + $smallerThan10->contains(9), + 'ThresholdRange::contains() identifies nine as greater than or equal to ten' + ); + $this->assertTrue( + $smallerThan10->contains(PHP_INT_MAX), + 'ThresholdRange::contains() identifies infinity as outside the range 10..~' + ); + + $greaterThan10 = ThresholdRange::fromString('~:10'); + $this->assertFalse( + $greaterThan10->contains(11), + 'ThresholdRange::contains() identifies eleven as smaller than or equal to ten' + ); + $this->assertTrue( + $greaterThan10->contains(~PHP_INT_MAX), + 'ThresholdRange::contains() identifies negative infinity as outside the range ~..10' + ); + } + + public function testFromStringProperlyParsesInclusiveRanges() + { + $inside0And10 = ThresholdRange::fromString('@10'); + $this->assertSame( + 0.0, + $inside0And10->getMin(), + 'ThresholdRange::fromString() does not identify zero as default minimum for inclusive ranges' + ); + $this->assertSame( + 10.0, + $inside0And10->getMax(), + 'ThresholdRange::fromString() does not identify ten as explicit maximum for inclusive ranges' + ); + $this->assertTrue( + $inside0And10->isInverted(), + 'ThresholdRange::fromString() identifies inclusive ranges as double exclusive' + ); + + $inside10And20 = ThresholdRange::fromString('@10:20'); + $this->assertSame( + 10.0, + $inside10And20->getMin(), + 'ThresholdRange::fromString() does not identify ten as explicit minimum for inclusive ranges' + ); + $this->assertSame( + 20.0, + $inside10And20->getMax(), + 'ThresholdRange::fromString() does not identify twenty as explicit maximum for inclusive ranges' + ); + $this->assertTrue( + $inside10And20->isInverted(), + 'ThresholdRange::fromString() identifies inclusive ranges as double exclusive' + ); + + $greaterThan10 = ThresholdRange::fromString('@10:'); + $this->assertSame( + 10.0, + $greaterThan10->getMin(), + 'ThresholdRange::fromString() does not identify ten as explicit minimum for inclusive ranges' + ); + $this->assertNull( + $greaterThan10->getMax(), + 'ThresholdRange::fromString() does not identify infinity as default maximum for inclusive ranges' + ); + $this->assertTrue( + $greaterThan10->isInverted(), + 'ThresholdRange::fromString() identifies inclusive ranges as single exclusive' + ); + + $smallerThan10 = ThresholdRange::fromString('@~:10'); + $this->assertNull( + $smallerThan10->getMin(), + 'ThresholdRange::fromString() does not identify infinity as explicit minimum for inclusive ranges' + ); + $this->assertSame( + 10.0, + $smallerThan10->getMax(), + 'ThresholdRange::fromString() does not identify ten as explicit maximum for inclusive ranges' + ); + $this->assertTrue( + $smallerThan10->isInverted(), + 'ThresholdRange::fromString() identifies inclusive ranges as single exclusive' + ); + } + + /** + * @depends testFromStringProperlyParsesInclusiveRanges + */ + public function testContainsCorrectlyEvaluatesInclusiveRanges() + { + $inside0And10 = ThresholdRange::fromString('@10'); + $this->assertFalse( + $inside0And10->contains(10), + 'ThresholdRange::contains() identifies ten as greater than ten' + ); + $this->assertTrue( + $inside0And10->contains(11), + 'ThresholdRange::contains() identifies eleven as smaller than or equal to ten' + ); + $this->assertTrue( + $inside0And10->contains(-1), + 'ThresholdRange::contains() identifies negative values as greater than or equal to zero' + ); + + $inside10And20 = ThresholdRange::fromString('@10:20'); + $this->assertFalse( + $inside10And20->contains(20), + 'ThresholdRange::contains() identifies twenty as greater than twenty' + ); + $this->assertTrue( + $inside10And20->contains(21), + 'ThresholdRange::contains() identifies twenty-one as smaller than or equal to twenty' + ); + $this->assertTrue( + $inside10And20->contains(9), + 'ThresholdRange::contains() identifies nine as greater than or equal to ten' + ); + + $greaterThan10 = ThresholdRange::fromString('@10:'); + $this->assertFalse( + $greaterThan10->contains(PHP_INT_MAX), + 'ThresholdRange::contains() identifies infinity as smaller than ten' + ); + $this->assertTrue( + $greaterThan10->contains(9), + 'ThresholdRange::contains() identifies nine as greater than or equal to ten' + ); + + $smallerThan10 = ThresholdRange::fromString('@~:10'); + $this->assertFalse( + $smallerThan10->contains(~PHP_INT_MAX), + 'ThresholdRange::contains() identifies negative infinity as greater than ten' + ); + $this->assertTrue( + $smallerThan10->contains(11), + 'ThresholdRange::contains() identifies eleven as smaller than or equal to ten' + ); + } + + public function testFromStringProperlyParsesEmptyThresholds() + { + $emptyThreshold = ThresholdRange::fromString(''); + $this->assertNull( + $emptyThreshold->getMin(), + 'ThresholdRange::fromString() does not identify negative infinity as implicit minimum for empty strings' + ); + $this->assertNull( + $emptyThreshold->getMax(), + 'ThresholdRange::fromString() does not identify infinity as implicit maximum for empty strings' + ); + $this->assertFalse( + $emptyThreshold->isInverted(), + 'ThresholdRange::fromString() identifies empty strings as inclusive ranges rather than exclusive' + ); + } + + /** + * @depends testFromStringProperlyParsesEmptyThresholds + */ + public function testContainsEvaluatesEverythingToTrueForEmptyThresholds() + { + $emptyThreshold = ThresholdRange::fromString(''); + $this->assertTrue( + $emptyThreshold->contains(0), + 'ThresholdRange::contains() does not identify zero as valid without any threshold' + ); + $this->assertTrue( + $emptyThreshold->contains(10), + 'ThresholdRange::contains() does not identify ten as valid without any threshold' + ); + $this->assertTrue( + $emptyThreshold->contains(PHP_INT_MAX), + 'ThresholdRange::contains() does not identify infinity as valid without any threshold' + ); + $this->assertTrue( + $emptyThreshold->contains(~PHP_INT_MAX), + 'ThresholdRange::contains() does not identify negative infinity as valid without any threshold' + ); + } + + public function testInvalidThresholdNotationsAreRenderedAsIs() + { + $this->assertSame( + ':', + (string) ThresholdRange::fromString(':') + ); + $this->assertSame( + '~:', + (string) ThresholdRange::fromString('~:') + ); + $this->assertSame( + '20:10', + (string) ThresholdRange::fromString('20:10') + ); + $this->assertSame( + '10@', + (string) ThresholdRange::fromString('10@') + ); + $this->assertSame( + 'foo', + (string) ThresholdRange::fromString('foo') + ); + } +} From e7bcf1393cfce61f17a8fd5f7c7650f650005400 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 17 Jan 2023 17:45:15 +0100 Subject: [PATCH 2/3] ThresholdRange: Fix type declarations of `setMax` and `setMin` --- library/Icingadb/Util/ThresholdRange.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icingadb/Util/ThresholdRange.php b/library/Icingadb/Util/ThresholdRange.php index c92842d07..fefed75ba 100644 --- a/library/Icingadb/Util/ThresholdRange.php +++ b/library/Icingadb/Util/ThresholdRange.php @@ -92,7 +92,7 @@ public static function fromString(string $rawRange): self * * @return $this */ - public function setMin(float $min): self + public function setMin(?float $min): self { $this->min = $min; return $this; @@ -115,7 +115,7 @@ public function getMin() * * @return $this */ - public function setMax(float $max): self + public function setMax(?float $max): self { $this->max = $max; return $this; From 5c20275a5a0ba7b0739f4d6bc7a1e57db91ccb0c Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 19 Jan 2023 11:03:22 +0100 Subject: [PATCH 3/3] PerfDataSet: Fix that invalid quoted labels throw an error --- library/Icingadb/Util/PerfDataSet.php | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/library/Icingadb/Util/PerfDataSet.php b/library/Icingadb/Util/PerfDataSet.php index 6a18d9919..df3139304 100644 --- a/library/Icingadb/Util/PerfDataSet.php +++ b/library/Icingadb/Util/PerfDataSet.php @@ -115,7 +115,7 @@ protected function readLabel(): string $this->skipSpaces(); if (in_array($this->perfdataStr[$this->parserPos], array('"', "'"))) { $quoteChar = $this->perfdataStr[$this->parserPos++]; - $label = $this->readUntil($quoteChar); + $label = $this->readUntil($quoteChar, '='); $this->parserPos++; if ($this->perfdataStr[$this->parserPos] === '=') { @@ -133,17 +133,30 @@ protected function readLabel(): string /** * Return all characters between the current parser position and the given character * - * @param string $stopChar The character on which to stop + * @param string $stopChar The character on which to stop + * @param string $backtrackOn The character on which to backtrack * - * @return string + * @return string */ - protected function readUntil(string $stopChar): string + protected function readUntil(string $stopChar, string $backtrackOn = null): string { $start = $this->parserPos; - while ($this->parserPos < strlen($this->perfdataStr) && $this->perfdataStr[$this->parserPos] !== $stopChar) { + $breakCharEncounteredAt = null; + $stringExhaustedAt = strlen($this->perfdataStr); + while ($this->parserPos < $stringExhaustedAt) { + if ($this->perfdataStr[$this->parserPos] === $stopChar) { + break; + } elseif ($breakCharEncounteredAt === null && $this->perfdataStr[$this->parserPos] === $backtrackOn) { + $breakCharEncounteredAt = $this->parserPos; + } + $this->parserPos++; } + if ($breakCharEncounteredAt !== null && $this->parserPos === $stringExhaustedAt) { + $this->parserPos = $breakCharEncounteredAt; + } + return substr($this->perfdataStr, $start, $this->parserPos - $start); }