piotrmurach / tty-progressbar

Display a single or multiple progress bars in the terminal.
https://ttytoolkit.org
MIT License
424 stars 24 forks source link

Multi progress bar does not update drawing current / total values properly #37

Closed elquimista closed 3 years ago

elquimista commented 4 years ago

Describe the problem

Multi progress bar's top_bar does not update drawing current / total values properly with registering new child bars.

Steps to reproduce the problem

require 'bundler/inline'

gemfile true do
  source 'http://rubygems.org'
  gem 'tty-progressbar'
end

class FetchPackages
  def run
    @multibar = TTY::ProgressBar::Multi.new('Fetching packages (:current/:total)')
    validate_local_cache
    fetch
  end

  def validate_local_cache
    bar = @multibar.register('Validating local cache', total: 1)
    sleep 2
    bar.advance
  end

  def fetch
    bar = @multibar.register('Fetching :percent (:current/:total)', total: 20)
    20.times do
      sleep 0.5
      bar.advance
    end
  end
end

FetchPackages.new.run

Actual behaviour

┌ Fetching packages (1/1)
└── Validating local cache
└── Fetching 100% (20/20)

Expected behaviour

┌ Fetching packages (21/21)
├── Validating local cache
└── Fetching 100% (20/20)

Describe your environment

piotrmurach commented 4 years ago

Thanks for using the gem and reporting this issue!

Great spot. Do you have time to submit PR? Even a test demonstrating the issue will be good?

elquimista commented 4 years ago

Honestly I don't have time for a PR, but I'll take a look into this when I get a chance.

elquimista commented 4 years ago

If I insert byebug after the 2nd register call in #fetch method, and check @multibar.total it correctly says 21 so I think this is issue with redrawing the top bar when its children are updated.

piotrmurach commented 4 years ago

Some notes for people who may be reading this issue. The original intention behind multi progress bars was to track the execution of multiple tasks/jobs concurrently. The provided example is a synchronous set of steps that the gem hasn't been designed for. The following would work as expected when converted to separate threads of execution:

class FetchPackages
  def run
    @multibar = TTY::ProgressBar::Multi.new('Fetching packages (:current/:total)')
    th_valid = Thread.new { validate_local_cache }
    th_fetch = Thread.new { fetch }
    th_valid.join
    th_fetch.join
  end
  ...

Though this would fail in the given scenario as the fetching would happen before validation. There's a new resume api that will help stop and resume single progress bars. This call will automatically trigger when a multi-bar is in use.