// Fenrir console screen reader. // Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program. If not, see . package progress import ( "math" "strings" "testing" "time" ) func TestPythonProgressFormats(t *testing.T) { for _, testCase := range []struct { text string percentage float64 interval time.Duration }{ {"Downloading 0%", 0, 0}, {"Downloading 25.5%", 25.5, 0}, {"100%", 100, 0}, {"15/100", 15, 0}, {"3 of 10", 30, 0}, {"[#### ]", 50, 0}, {"[====> ]", 50, 0}, {"|█████ |", 50, 0}, {"[▌ ]", 12.5, 0}, {" 50 100M 50 50M 0 0 10M 0 0:00:10 0:00:05 0:00:05 10M", 50, 0}, {"frame= 10 fps=25 size=100kB time=00:00:03.50 bitrate=200kbits/s speed=1.0x", 50, 2 * time.Second}, {"remaining: 12s", 50, 2 * time.Second}, {"generating 20 tokens", 50, 2 * time.Second}, {"1024 bytes copied, 1 s, 1 kB/s", 50, 2 * time.Second}, {"2.5 MiB 1.2 MiB/s 00:02", 50, 2 * time.Second}, {"Loading...", 50, 2 * time.Second}, {"⠋ preparing", 50, time.Second}, {"🌒 working", 50, time.Second}, {"◑", 50, time.Second}, {"✻ Generating answer… (esc to interrupt)", 50, time.Second}, {"Working (ctrl+c to interrupt)", 50, time.Second}, {"• Checking files…", 50, time.Second}, {"◦ Working on files", 50, time.Second}, {"🔄 Thinking 3s", 50, 1500 * time.Millisecond}, {"🔄 Thinking 23s", 50, 2 * time.Second}, {"● Task Running job", 50, 1500 * time.Millisecond}, } { t.Run(testCase.text, func(t *testing.T) { got, ok := Detect(testCase.text) if !ok || math.Abs(got.Percentage-testCase.percentage) > 0.001 || got.Interval != testCase.interval { t.Fatalf("got %+v, %v", got, ok) } }) } } func TestFalsePositives(t *testing.T) { for _, text := range []string{ "https://example.invalid/50%", "www.example.invalid/3/10", "humidity 50%", "CPU usage 70%", "weather 30%", "-1%", "1000%", "1.2.50%", "Page 3 of 10", "chapter 1/5", "10/25/2026", "5/0", "20/10", "1/1001", "[#channel]", "[item]", "[*]", "[--]", "10 20 30 40 50 60", "Speed", "normal text", "frame=10 fps=25 size=20", "150 100M 150 150M 0 0 10M 0 0:00:10 0:00:05 0:00:05 10M", "50 100M 30 30M 0 0 10M 0 0:00:10 0:00:05 0:00:05 10M", } { if got, ok := Detect(text); ok { t.Errorf("false positive %q: %+v", text, got) } } } func TestExplicitRows(t *testing.T) { for _, text := range []string{"package 50% [#### ]", "package 50% |#### |", "package 50% 2 MiB/s", "frame=10 time=00:01:20 fps=25"} { if !explicit(strings.Repeat("long-name", 30) + " " + text) { t.Error(text) } } for _, text := range []string{"50%", "Speed", "https://example.invalid 50% [#### ]", "normal table 1 2 3"} { if explicit(text) { t.Error(text) } } }