300]); // thold_rrd_last() returns whatever `rrdtool last` printed. CactiStubs::willReturn('rrdtool_execute', '1700000000'); } /** * @param array $fetch * * @return void */ private function rrdReturns(array $fetch) { CactiStubs::willReturn('rrdtool_function_fetch', $fetch); } /** * @return void */ public function testTheRequestedDataSourceValueIsReturned(): void { $this->rrdReturns([ 'data_source_names' => ['traffic_in', 'traffic_out'], 'values' => [['1700000000' => 10.0], ['1700000000' => 20.0]], ]); $this->assertSame(20.0, get_current_value(4, 'traffic_out')); } /** * array_search() returns false, not null, so a guard written against null * let the miss through and PHP then read index 0 — the first data source. * * @return void */ public function testUnknownDataSourceReturnsZeroRatherThanTheFirstOne(): void { $this->rrdReturns([ 'data_source_names' => ['traffic_in', 'traffic_out'], 'values' => [['1700000000' => 10.0], ['1700000000' => 20.0]], ]); $this->assertSame(0, get_current_value(4, 'upper_limit')); } /** * @return void */ public function testFirstDataSourceIsStillReachableByName(): void { $this->rrdReturns([ 'data_source_names' => ['traffic_in', 'traffic_out'], 'values' => [['1700000000' => 10.0], ['1700000000' => 20.0]], ]); $this->assertSame(10.0, get_current_value(4, 'traffic_in')); } /** * @return void */ public function testMissingDataSourceNamesReturnsZero(): void { $this->rrdReturns([]); $this->assertSame(0, get_current_value(4, 'traffic_in')); CactiStubs::reset(); CactiStubs::willReturn('db_fetch_row_prepared', ['rrd_step' => 300]); CactiStubs::willReturn('rrdtool_execute', '1700000000'); $this->rrdReturns([]); $this->assertSame('', get_current_value(4, 'traffic_in', 0, '')); } /** * @return void */ public function testMissingValuesReturnsZero(): void { $this->rrdReturns(['data_source_names' => ['traffic_in']]); $this->assertSame(0, get_current_value(4, 'traffic_in')); CactiStubs::reset(); CactiStubs::willReturn('db_fetch_row_prepared', ['rrd_step' => 300]); CactiStubs::willReturn('rrdtool_execute', '1700000000'); $this->rrdReturns(['data_source_names' => ['traffic_out'], 'values' => [['1700000000' => 20.0]]]); $this->assertSame('', get_current_value(4, 'traffic_in', 0, '')); } /** * @return void */ public function testEmptyValueSeriesReturnsZero(): void { $this->rrdReturns([ 'data_source_names' => ['traffic_in'], 'values' => [[]], ]); $this->assertSame(0, get_current_value(4, 'traffic_in')); } /** * A missing or unreadable RRD makes `rrdtool last` print nothing, which * used to reach the timestamp arithmetic as an empty string and fatal. * * @return void */ public function testUnreadableRrdReturnsZeroRatherThanThrowing(): void { CactiStubs::reset(); CactiStubs::willReturn('db_fetch_row_prepared', ['rrd_step' => 300]); CactiStubs::willReturn('rrdtool_execute', ''); $this->rrdReturns([]); $this->assertSame(0, get_current_value(4, 'traffic_in')); } /** * @return void */ public function testValueIsRoundedToFourDecimals(): void { $this->rrdReturns([ 'data_source_names' => ['traffic_in'], 'values' => [['1700000000' => 1.23456789]], ]); $this->assertSame(1.2346, get_current_value(4, 'traffic_in')); } /** * A stored 'U'/'nan'/blank reading must stay missing rather than coerce * to zero, the same contract as an empty value series above. * * @return void */ public function testNonNumericStoredValueReturnsTheMissingValue(): void { $this->rrdReturns([ 'data_source_names' => ['traffic_in'], 'values' => [['1700000000' => 'U']], ]); $this->assertSame(0, get_current_value(4, 'traffic_in')); CactiStubs::reset(); CactiStubs::willReturn('db_fetch_row_prepared', ['rrd_step' => 300]); CactiStubs::willReturn('rrdtool_execute', '1700000000'); $this->rrdReturns(['data_source_names' => ['traffic_in'], 'values' => [['1700000000' => 'U']]]); $this->assertSame('', get_current_value(4, 'traffic_in', 0, '')); } }