From 1c99ff8350d8b69b65bc36efa440ecfd14c6f9b9 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:43:41 +0000 Subject: [PATCH] Implement adaptive ETA calculation based on observed segment durations Co-authored-by: ashaduri <2302268+ashaduri@users.noreply.github.com> --- .gitignore | 1 + src/applib/selftest.cpp | 31 ++++++++++++++++++++++++++++++- src/applib/selftest.h | 8 ++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e2f564f..561b197 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ Thumbs.db /po/gsmartcontrol.pot /po/*.gmo +build/ diff --git a/src/applib/selftest.cpp b/src/applib/selftest.cpp index ce01ee6..50aa557 100644 --- a/src/applib/selftest.cpp +++ b/src/applib/selftest.cpp @@ -93,7 +93,30 @@ std::chrono::seconds SelfTest::get_remaining_seconds() const if (total <= 0s) return -1s; // unknown - const double gran = (double(total.count()) / 9.); // seconds per 10% + const double gran = (double(total.count()) / 9.); // seconds per 10% (drive estimate) + + // Use adaptive estimation if we have observed at least one completed segment + if (!segment_durations_.empty()) { + // Calculate average duration of observed segments + double sum = 0.0; + for (const auto& duration : segment_durations_) { + sum += duration; + } + const double avg_segment_duration = sum / segment_durations_.size(); + + // Estimate remaining time based on observed average and remaining segments + // remaining_percent_ goes from 100 (start) to 0 (end), in 10% decrements + const int8_t remaining_segments = (remaining_percent_ + 9) / 10; // round up + const double estimated_remaining = avg_segment_duration * remaining_segments - timer_.elapsed(); + + const auto rem_rounded = static_cast(std::round(estimated_remaining)); + if (rem_rounded < 0) { + return -1s; // estimate exhausted; return unknown + } + return std::chrono::seconds(rem_rounded); + } + + // Fall back to drive's initial estimate when we don't have observed data yet // since remaining_percent_ may be manually set to 100, we limit from the above. const double rem_seconds_at_last_change = std::min(double(total.count()), gran * remaining_percent_ / 10.); const double rem = rem_seconds_at_last_change - timer_.elapsed(); @@ -499,6 +522,12 @@ hz::ExpectedVoid SelfTest::update(const std::shared_ptr< // and reaches 00% on completion. That's 9 pieces. if (status_ == SelfTestStatus::InProgress) { if (remaining_percent_ != last_seen_percent_) { + // Record the duration of the completed segment for adaptive ETA calculation + if (last_seen_percent_ != -1 && last_seen_percent_ != 90) { + // Don't record the initial 100->90 transition (no real work done yet) + const double elapsed = timer_.elapsed(); + segment_durations_.push_back(elapsed); + } last_seen_percent_ = remaining_percent_; timer_.start(); // restart the timer } diff --git a/src/applib/selftest.h b/src/applib/selftest.h index 1272b6c..8ce4298 100644 --- a/src/applib/selftest.h +++ b/src/applib/selftest.h @@ -18,6 +18,7 @@ Copyright: #include #include #include +#include #include "storage_device.h" #include "command_executor.h" @@ -126,6 +127,12 @@ class SelfTest { /// Get estimated time of completion for the test. + /// The estimation uses an adaptive algorithm: + /// - Initially uses the drive's reported test duration estimate + /// - After completing one or more 10% segments, switches to using the observed + /// average segment duration to predict remaining time + /// - This provides more accurate ETAs when the drive's estimate is inaccurate + /// (e.g., under load or with drives that consistently under/overestimate) /// \return -1 if N/A or unknown (including when the drive's estimated duration has been /// exceeded without a percentage change, which means the estimate was inaccurate). /// Note that 0 is a valid value meaning the test is finishing right now. @@ -182,6 +189,7 @@ class SelfTest { std::chrono::seconds poll_in_seconds_ = std::chrono::seconds(-1); ///< The user is asked to poll after this much seconds have passed. Glib::Timer timer_; ///< Counts time since the last percent change + std::vector segment_durations_; ///< Actual durations of completed 10% segments (in seconds), for adaptive ETA calculation };