diff --git a/src/Progressable.php b/src/Progressable.php index 4492077..3d01509 100644 --- a/src/Progressable.php +++ b/src/Progressable.php @@ -292,9 +292,7 @@ public function isOverallComplete(): bool { * Get the estimated time remaining in seconds for the overall progress. */ public function getOverallEstimatedTimeRemaining(): ?int { - $overallProgress = $this->getOverallProgress(); - - if ($overallProgress >= 100) { + if ($this->isOverallComplete()) { return 0; } @@ -304,11 +302,15 @@ public function getOverallEstimatedTimeRemaining(): ?int { return null; } + $totalProgress = array_sum(array_column($progressData, 'progress')); + $totalCount = count($progressData); + $unroundedOverallProgress = $totalCount > 0 ? $totalProgress / $totalCount : 0; + $startTimes = array_filter(array_column($progressData, 'start_time'), function ($time) { return $time !== null; }); - if (empty($startTimes) || $overallProgress <= 0) { + if (empty($startTimes) || $unroundedOverallProgress <= 0) { return null; } @@ -319,10 +321,10 @@ public function getOverallEstimatedTimeRemaining(): ?int { return null; } - $rate = $overallProgress / $elapsed; - $remainingProgress = 100 - $overallProgress; + $rate = $unroundedOverallProgress / $elapsed; + $remainingProgress = 100 - $unroundedOverallProgress; - return (int) round($remainingProgress / $rate); + return (int) ceil($remainingProgress / $rate); } /** @@ -352,7 +354,7 @@ public function getEstimatedTimeRemaining(): ?int { $rate = $this->progress / $elapsed; // progress per second $remainingProgress = 100 - $this->progress; - return (int) round($remainingProgress / $rate); + return (int) ceil($remainingProgress / $rate); } /** diff --git a/tests/OverallEtaTest.php b/tests/OverallEtaTest.php index 033ee83..1449237 100644 --- a/tests/OverallEtaTest.php +++ b/tests/OverallEtaTest.php @@ -74,4 +74,31 @@ public function test_overall_eta_is_zero_when_complete(): void { $this->assertEquals(0, $this->getOverallEstimatedTimeRemaining()); } + + public function test_overall_eta_does_not_return_zero_prematurely_due_to_rounding(): void { + Carbon::setTestNow(Carbon::now()); + + $uniqueName = 'test_overall_eta_rounding_'.$this->testId; + $this->setOverallUniqueName($uniqueName); + $this->setLocalKey('process_1'); + $this->setPrecision(0); + $this->setLocalProgress(0); // Set start time + + $obj2 = new class { + use Progressable; + }; + $obj2->setOverallUniqueName($uniqueName); + $obj2->setLocalKey('process_2'); + $obj2->setPrecision(0); + $obj2->setLocalProgress(0); // Set start time + + Carbon::setTestNow(Carbon::now()->addSeconds(10)); + + $this->setLocalProgress(100); + $obj2->setLocalProgress(99.4); + + // Overall progress with precision 0 is round((100+99.4)/2) = round(99.7) = 100 + // It shouldn't return 0 for ETA yet! + $this->assertGreaterThan(0, $this->getOverallEstimatedTimeRemaining()); + } }