Commit Graph
1142 Commits
Author SHA1 Message Date
Dark-Alex-17 fbf711f428 test(lidarr): extract and cover the track row style branch
The snapshot harness renders to a plain string, so colour and text modifiers never reach the assertion. The track row's downloaded and missing arms were therefore invisible to the suite: swapping either arm for the other, or inverting the has_file condition, all left the full run green.

Move the branch into decorate_track_row_with_style, mirroring the row decorators already extracted in the other library detail views, and unit test both arms directly against Row equality. All three mutations now fail the suite.
2026-09-10 16:19:25 -06:00
Dark-Alex-17 db8e97a700 fix(sonarr): stop marking seasons fully downloaded when no episodes are known
A monitored season rendered in the fully downloaded colour whenever Sonarr returned no statistics for it, and whenever it genuinely had zero known episodes. Both cases compared zero against zero and short circuited to the downloaded style, claiming the user had every episode of a season that was only announced, or that had never been scanned.

Seasons whose statistics are absent now render as indeterminate, matching the series row and the equivalent album and book rows in the other trees, and the downloaded style now requires a positive episode count.
2026-09-10 15:09:40 -06:00
Dark-Alex-17 c069ed4a9b test(sonarr): extract and cover the season row style branches
Row styling is invisible to the snapshot harness, because render_to_string_with_app returns a plain String and discards colour and modifiers. The season row style branch was therefore entirely uncovered, and any arm could be swapped for another without a single test noticing.

decorate_season_row_with_style now holds that branch, matching decorate_series_row_with_style in the library view. The extraction is behaviour preserving: the branch is token identical to the one it replaces, the statistics fallback resolves the same way, and no snapshot changed. The season row deliberately has no positive episode count guard, so a season without statistics falls back to zero of zero episodes and reads as downloaded; a test now pins that.

Verified by mutation. Against the previous tree all seven mutations left the suite green: replacing each of the unmonitored, downloaded, unreleased and both missing arms, flipping the episode file count comparison, and inverting the monitored check. Against the new tests every one of those seven fails.
2026-09-10 13:52:17 -06:00
Dark-Alex-17 3227495947 test(lidarr): extract and cover the album row style branches
Row styling is invisible to the snapshot harness, because render_to_string_with_app returns a plain String and discards colour and modifiers. The album row style branch was therefore entirely uncovered, and any arm could be swapped for another without a single test noticing.

decorate_album_row_with_style now holds that branch, matching decorate_artist_row_with_style in the library view and decorate_book_row_with_style in the Readarr author details view. The extraction is behaviour preserving: the branch is token identical to the one it replaces, and no snapshot changed.

Verified by mutation. Against the previous tree all eight mutations left the suite green: replacing each of the unmonitored, downloaded, unreleased, both missing and indeterminate arms, flipping the track file count comparison, and inverting the monitored check. Against the new tests every one of those eight fails.
2026-09-10 13:51:25 -06:00
Dark-Alex-17 243b22a426 fix: keep a space between overview words that span a line break
The collapsed one-line overview on the Lidarr artist, Sonarr series, and Readarr author details views replaced every line break and tab with an empty string. Words on either side of a paragraph break fused together, rendering as "20th century.She was born in Ohio".

Collapse each run of whitespace to a single space instead, and trim the result. Handling a whole run at a time keeps a Windows line ending, an indented paragraph, or a space that already precedes a line break from producing two spaces.

The overview popups are unaffected: they receive the raw text and keep their paragraph breaks.
2026-09-10 13:27:02 -06:00
Dark-Alex-17 caafe1602e test(radarr): name the updates loading test after its neighbours
The Radarr system details tests prefix their loading cases with renders_. This one was copied verbatim from the other trees and did not match. Rename it and its snapshot to fit, leaving the sibling trees alone.
2026-09-10 13:07:54 -06:00
Dark-Alex-17 65ba37c09a fix: parse the log level from the unscrolled log text
draw_logs read the level from the log line's Display output, which drops the first offset characters. Once a log line is scrolled, the field at index 1 is no longer the level: past the timestamp it yields the logger name and colours the row wrong, and past the final delimiter the index is out of bounds and panics, taking the TUI down.

Parse log.text instead, matching what draw_logs_popup already does. The displayed text is unchanged.
2026-09-10 13:06:04 -06:00
Dark-Alex-17 c525fb9a4e feat(radarr): render the movie overview popup at the large size
The movie overview was the only overview popup drawn at the small size;
the sonarr, lidarr and readarr overview popups all use the large size.
The small size wrapped longer overviews sooner and gave the text roughly
a quarter of the space the other trees allow.

Draw it at the large size to match them. The wider popup now covers the
collection details behind it, as the other overview popups do.
2026-09-10 12:40:59 -06:00
Dark-Alex-17 e79cc7a12d fix(radarr): show a loading block while the updates popup refreshes
The updates popup only checked whether its text was empty, so during a
refresh it kept rendering the previously fetched text. Opening the system
updates view dispatches a request for the update notes, so the popup could
sit on stale content until the response landed.

Guard on the loading flag as well, matching the sonarr, lidarr and readarr
updates popups, and add the loading-state test those trees have so both
halves of the condition are exercised. The pre-existing empty-updates test
never reaches the second half on its own.
2026-09-10 12:35:56 -06:00
Dark-Alex-17 12dbbf0b1b test: give the statistics fixtures distinct file and total counts
The artist, album and season statistics fixtures set the file count equal to the total count, so the columns rendering the pair showed the same number twice and swapping the two fields changed nothing. The JSON fixtures move with them so the round trip assertions still hold.
2026-09-10 01:10:41 -06:00
Dark-Alex-17 1a5e5c3145 test: give the delete flags distinct values where params are asserted
The delete params tests set the delete files flag and the list exclusion flag both true, so swapping the two fields in the params builder produced identical params and no test noticed. The tests that assert a reset still set both true, since that is what proves both flags are cleared.
2026-09-10 01:04:07 -06:00
Dark-Alex-17 5fa521ba7f test: cover indexer settings drawn over the indexers table
IndexersUi dispatches to IndexerSettingsUi, but no test rendered that route through the parent, so deleting the dispatch arm in radarr, sonarr and lidarr left the whole suite green.
2026-09-10 00:57:55 -06:00
Dark-Alex-17 f7c67ad58b test(radarr): cover the updates popup empty state
Radarr was the only tree without a test rendering draw_updates_popup with no update text, leaving its loading branch unreachable from the suite. Forcing the guard true went undetected.
2026-09-10 00:53:07 -06:00
Dark-Alex-17 7e8072133c test: pin the updates popup loading guard in sonarr and lidarr
draw_updates_popup shows the loading block when update text is already present but a refresh is still in flight. Both existing tests fed empty update text, so they only exercised the first half of the guard and dropping the is_loading conjunct went undetected.
2026-09-10 00:49:42 -06:00
Dark-Alex-17 e622be3288 test: assert delegated handlers pop exactly one navigation stack frame
test_handler_delegation! asserted only that the current route equalled the
base block after Esc. When the delegated block was itself the base block the
stack read [base, base], so that assertion held whether or not the sub-handler
popped, and the case asserted nothing.

Capture the navigation stack depth before delegating and assert it drops by
exactly one, which also catches a handler that pops and then re-pushes. Depth
is read through a test-only accessor on App; the navigation_stack field stays
private.

Main tab blocks clear errors on Esc rather than popping, by design, so they
cannot be probed this way at all. Drop them from the delegation value lists;
the remaining values in each list already exercise the same dispatch arm.
2026-09-10 00:41:07 -06:00
Dark-Alex-17 08018e4457 test(readarr): give the quality and metadata profile fixtures distinct values
ReadarrData::test_default_fully_populated seeded both profile maps with the same id and the same name, and gave the author fixture the same id for both profiles. The library table renders the two profiles in adjacent columns, so both read Standard and swapping the maps produced identical output.

The maps now use the disjoint ids and the vocabulary the root folder and add author tests already use, so the columns read Standard and Comprehensive.

Verified by swapping the two map lookups where the authors table is built. That swap survived the whole suite before this change and is now caught by sixteen snapshots.
2026-09-09 17:03:28 -06:00
Dark-Alex-17 78f5045844 test: pin extract_task_name against a real selected task
Both tests seeded a single default task and asserted the extracted name equalled the default task name, so a body that ignored the table entirely and returned the default satisfied them. Radarr and Readarr already seed distinct named tasks and select one; these two now follow that shape.

Verified by replacing both bodies with a bare default task name. The Lidarr test could not have failed before, since it compared the default against itself, and the run confirms it is the only Lidarr test that catches the change. The Sonarr body was already caught by the two start task prompt tests, so there the dedicated test is strengthened rather than newly load bearing.
2026-09-09 17:00:49 -06:00
Dark-Alex-17 e51b9d72b1 test(radarr): pin which table build_add_movie_body reads from
The handler fixtures for a collection movie and a searched movie both carried tmdb_id 1234 and the title Test, so the two branches of build_add_movie_body were value equivalent. The builder test also populated only the table its branch needed, so swapping the branches was caught by an unwrap panic on the empty table rather than by any assertion on the values.

The searched movie fixture now has its own identity, and the builder test seeds both tables so the branch is chosen against real competing data. Swapping the branches now fails with a value diff naming the wrong tmdb_id and title.

Only the handler copy of the fixture is changed. The network copy deliberately shares tmdb_id 1234 with the movie fixture, which is what drives the already in library marker in the add movie snapshots, so changing it there would have silently removed that marker.
2026-09-09 16:55:28 -06:00
Dark-Alex-17 b9d96ce235 test(readarr): give each history fixture row a distinct quality
Two of the three rows carried the quality EPUB, so sorting by quality left them tied and any reordering confined to that pair was invisible. The rows now use EPUB, AZW3 and MOBI, matching the vocabulary the history UI fixture already uses.

This one is defensive: no natural mutation of the quality comparator flipped from surviving to failing, because two distinct values already separated the cross-field cases. The tie is removed on principle rather than to close a demonstrated hole.
2026-09-09 16:46:50 -06:00
Dark-Alex-17 3e414f9f0c test: give the CLI indexer settings tests distinct field values
The Radarr, Sonarr and Lidarr tests for editing all indexer settings expected every numeric field to be 1 and returned every previous field as 2. Because the fields were mutually equal, transposing any two of availability_delay, maximum_size, minimum_age, retention and rss_sync_interval in the CLI parameter builder produced an identical request and no test could tell. Radarr additionally expected both boolean flags to hold the same value, hiding a transposition of those two.

Readarr already used distinct values and was the model followed here.

Verified by transposing minimum_age and retention in all four CLI parameter builders at once: before this change only the two Readarr tests failed, while Radarr, Sonarr and Lidarr stayed green. After it, the same mutation is caught by five tests across the three trees.
2026-09-09 16:43:25 -06:00
Dark-Alex-17 f99d4f61f4 test: give the tree-local IndexerSettings fixtures distinct field values
Sonarr and Lidarr each keep their own copy of the shared fixture, both with minimum_age and retention set to 1, so transposing those two fields stayed invisible in either tree. Radarr built its fixture from IndexerSettings::default(), leaving availability_delay, maximum_size, minimum_age and retention all 0: four mutually transposable fields, none of which could distinguish a threaded value from a hardcoded 0.

The no-op guard test seeded IndexerSettings::default() as its sentinel, which cannot show whether the guard held or the field was simply never written. It now seeds a distinct stale_indexer_settings(), mirroring the Readarr convention.

Verified by transposing minimum_age and retention in the Lidarr indexer settings UI, now caught by its snapshot, and by inverting the Radarr no-op guard, now caught by both GetAllIndexerSettings tests.
2026-09-09 16:38:36 -06:00
Dark-Alex-17 d08f7d4b4e test: give the shared IndexerSettings fixture distinct field values
minimum_age and retention were both 1, so transposing them in any code that reads both rendered identically and no test could tell the difference. id was also 1, which cannot distinguish a correctly threaded value from a hardcode.

Verified by transposing minimum_age and retention in the Sonarr indexer settings UI: that mutation survived the full suite before this change and is now caught by the settings snapshot.
2026-09-09 16:31:05 -06:00
Dark-Alex-17 cef1ad13af test(readarr): pin table search, filter and selection behaviour
Readarr handler tests never asserted which block the search and filter keys push, so transposing searching_block and filtering_block in a TableHandlingConfig went undetected. History already had both tests; add the five missing search key tests and the three missing filter key tests, covering the authors, books, author history, editions and book history tables. The books and editions tables configure no filtering_block, so they get no filter test.

No test moved a table or list selection off index 0 either, which made current_selection() and items[0] indistinguishable. Select a non-zero index in the tests covering every handler that reads a selected row, adding a decoy entry at index 0 so existing expected values stay meaningful. The profile, monitor and root folder lists inside the add author, edit author and add root folder prompts are a separate gap that a table-only sweep misses, so those are covered too, as are the book files, editions and releases tables held on the book details modal.

Several fixtures also asserted against a default value, so the expected result was indistinguishable from an unset field. The add author, edit author and add root folder bodies now expect non-default monitor options, and the quality and metadata profile ids are drawn from disjoint id pairs so the two lookups cannot be transposed.
2026-09-09 16:22:51 -06:00
Dark-Alex-17 f141e53f30 test(lidarr): pin table search, filter and selection behaviour
The search and filter blocks a table configures were asserted nowhere, so
transposing them was invisible. Selections were also left at index 0 in
every test, which made current_selection() and items[0] indistinguishable,
including for the profile lists inside the edit and add prompts.
2026-09-09 15:51:10 -06:00
Dark-Alex-17 18a50ab97c test(sonarr): pin table search, filter and selection behaviour
Sonarr handler tests never asserted which block the search and filter keys
push, so transposing searching_block and filtering_block in a
TableHandlingConfig went undetected. Add a search key test for each of the
six searchable tables and a filter key test for each of the four filterable
ones, covering library, history, series details and season details.

No test moved a table or list selection off index 0 either, which made
current_selection() and items[0] indistinguishable. Select a non-zero index
in the tests covering every handler that reads a selected row, adding a
decoy entry at index 0 so existing expected values stay meaningful. The
modal list selections in the add and edit series prompts are a separate
gap that a table-only sweep misses, so those are covered too.

Several fixtures also asserted against a default value, so the expected
result was indistinguishable from an unset field. The system tasks now use
two distinct non-default task names, the season monitoring and manual
search expectations use distinct non-zero ids, and the add and edit series
bodies expect a non-default series type and monitor with distinct quality
and language profile ids so the two lookups cannot be transposed.
2026-09-09 11:25:34 -06:00
Dark-Alex-17 4cbec79334 test(radarr): pin table search, filter and selection behaviour
Radarr handler tests never asserted which block the search and filter keys
push, so transposing searching_block and filtering_block in a
TableHandlingConfig went undetected. Add a search key and a filter key test
for each of the three handlers that configure them: library, collections and
history.

No test moved a table or list selection off index 0 either, which made
current_selection() and items[0] indistinguishable. Select a non-zero index
in the tests covering every handler that reads a selected row, enriching
fixtures with a second, distinct entry where only one existed.

Several fixtures also asserted against a default value, so the expected
result was indistinguishable from an unset field. The system details tasks
now use two distinct non-default task names, and the add movie body expects
a non-default monitor and minimum availability.
2026-09-09 10:47:07 -06:00
Dark-Alex-17 c9244b0459 feat(radarr): make the collection movie overview popup scrollable
A collection movie's overview renders in a popup that does not scroll,
so a long overview overflows with no way to read the rest. The popup now
scrolls.

The popup owns its own ScrollableText, built from the selected collection
movie's overview when it opens and torn down on Esc so a reopened popup
starts at the top. Scrolling mutates only that owned state. The
background renders the Collection record's overview and the movies
table, neither of which carries a scroll offset, so they cannot scroll
along with the popup. The popup also gains the loading and absent
fallbacks the rest of the tree already has.

Give the collection movie fixture an overview distinct from its
collection's, and a multi-paragraph one, so the snapshots can tell the
two records apart and a scrolled popup is distinguishable from an
unscrolled one.
2026-09-08 22:07:22 -06:00
Dark-Alex-17 b3ce15a564 feat(sonarr): add a scrollable overview popup to series details
A series' overview renders as one line of a description box that does
not scroll, so a long overview overflows with no way to read the rest.
Pressing v on the series details Seasons tab now opens a popup holding
the full overview.

The popup owns its own ScrollableText, built fresh from the series'
overview when it opens and torn down on Esc so a reopened popup starts
at the top. Scrolling mutates only that owned state. The background
paragraph renders the Series record's overview, which carries no scroll
offset, so it cannot scroll along with the popup.

Cite the existing view keybinding from the series details context clues
and give the popup its own clue set so it does not inherit the hints of
the view underneath.

Enrich the shared series fixture to a multi-paragraph overview, and the
raw response fixture that encodes the same series, so a scrolled popup
is distinguishable from an unscrolled one.
2026-09-08 21:31:25 -06:00
Dark-Alex-17 ca04498717 feat(lidarr): add a scrollable overview popup to artist details
An artist's overview renders as one line of a description box that does
not scroll, so a long overview overflows with no way to read the rest.
Pressing v on the artist details Albums tab now opens a popup holding
the full overview.

The popup owns its own ScrollableText, built fresh from the artist's
overview when it opens and torn down on Esc so a reopened popup starts
at the top. Scrolling mutates only that owned state. The background
paragraph renders the Artist record's overview, which carries no scroll
offset, so it cannot scroll along with the popup.

Cite the existing view keybinding from the artist details context clues
and give the popup its own clue set so it does not inherit the hints of
the view underneath.

Enrich the shared artist fixture to a multi-paragraph overview, and the
blocklist response fixtures that embed the same artist, so a scrolled
popup is distinguishable from an unscrolled one.
2026-09-08 21:05:50 -06:00
Dark-Alex-17 27c458307f feat(readarr): add a scrollable overview popup to author details
An author's overview renders as the last line of a description box that
does not scroll, so a long overview overflows with no way to read the
rest. Pressing v on the author details Books tab now opens a popup
holding the full overview.

The popup owns its own ScrollableText, built fresh from the author's
overview when it opens and torn down on Esc so a reopened popup starts
at the top. Scrolling mutates only that owned state. The background
paragraph renders the Author record's overview, which carries no scroll
offset, so it cannot scroll along with the popup.

Add a view keybinding bound to v, cite it from the author details
context clues, and give the popup its own clue set so it does not
inherit the hints of the view underneath.
2026-09-08 18:05:52 -06:00
Dark-Alex-17 ee6035c75d test(readarr): cover the top level UI dispatch for Readarr
The top level ui() dispatch had no Readarr coverage, so removing its match arm let every test in the suite pass while the Readarr views rendered nothing at all.

Mirrors the Radarr set, which is the most complete of the three: it is the only one that also covers the notification failure popup.
2026-09-08 16:18:00 -06:00
Dark-Alex-17 3fd0a61737 fix: show confirmation prompt clues on delete prompts
Delete confirmation prompts had no arm in any of the context clue providers, so they fell through to the main tab's contextual help and displayed the library tab's hints, such as add, sort and edit, while the user was looking at a delete confirmation.

Map the delete block arrays in all four servarrs to the shared confirmation prompt clues, matching how the other prompts and selection modals are already wired.
2026-09-08 16:07:19 -06:00
Dark-Alex-17 720584ea17 fix: drop redundant references in format arguments
The borrows were passed straight to format!, which already takes its
arguments by reference. Clears the last of the compiler warnings.
2026-09-08 15:56:38 -06:00
Dark-Alex-17 59346973fc feat(readarr): add the indexers UI
Render the indexers table with its delete and test popups, the edit
indexer prompt with separate torrent and usenet field grids, the prompt
for configuring all indexer settings, and the test all indexers results
table. All four are dispatched from the Readarr UI.

Extract indexer_flag_text, decorate_test_result_row_with_style and the
two prompt highlight builders as named functions so their branches can
be unit tested. Rendering to a string discards colour, so a snapshot
cannot tell a success style from a failure style.
2026-09-08 10:38:52 -06:00
Dark-Alex-17 a7178a481b feat(readarr): add the indexers handlers
Add IndexersHandler and its three sub-handlers, EditIndexerHandler, IndexerSettingsHandler and TestAllIndexersHandler, and dispatch to them from ReadarrHandler.

IndexersHandler::accepts ORs in all three children, so the parent and the children land together. Splitting would leave eighteen blocks accepted but inert, with Esc falling through to handle_clear_errors instead of popping.

TestAllIndexersHandler::accepts compares the block for equality rather than consulting an array, since TestAllIndexers appears in no block array.
2026-09-08 10:10:25 -06:00
Dark-Alex-17 70441259a0 feat(readarr): add the system UI
The system tab renders the tasks table, the queued events table and the log list, and overlays the log details, tasks, queued events, task start confirmation and updates popups.

SystemUi and SystemDetailsUi ship together because SystemUi::accepts covers every block that only SystemDetailsUi draws. Landing them apart would advertise five blocks that nothing renders.

Log level extraction is pulled into a named function so it can be unit tested directly: the level only ever reaches the output as a colour, and the rendered snapshots are plain strings that discard it.
2026-09-08 09:51:55 -06:00
Dark-Alex-17 24c5dedd0c feat(readarr): add the system handlers
SystemHandler owns the System tab: tab movement, the refresh key, and the
five keys that open the queued events, logs, tasks, and updates views. It
delegates every system detail block to SystemDetailsHandler, which scrolls
the log, task, queued event, and update views and drives the start task
confirmation prompt.

Both land together because SystemHandler::accepts covers the system detail
blocks that only SystemDetailsHandler implements, so landing the parent
alone would accept those blocks while doing nothing with them.
2026-09-08 09:33:26 -06:00
Dark-Alex-17 58fe7d79ae feat(readarr): add the root folders UI
Renders the root folders table, the delete prompt and the add root
folder modal. The modal conveys its current step purely through
styling, which rendered output discards, so the step highlights are
computed by a separate function whose branches are asserted directly.
2026-09-08 09:15:37 -06:00
Dark-Alex-17 5e1f3bb7e3 feat(readarr): add the add root folder handler 2026-09-04 12:22:35 -06:00
Dark-Alex-17 a4a3c77586 feat(readarr): add the root folders handler 2026-09-04 12:11:00 -06:00
Dark-Alex-17 ee07a0289f feat(readarr): add the history UI
Renders the history table, its sort, search and filter prompts, both
empty result popups and the item details view. Snapshots cover a
non-default selection so that reading a row from a fixed index rather
than the current selection cannot pass unnoticed.
2026-09-04 09:49:36 -06:00
Dark-Alex-17 91c2613e65 feat(readarr): add the downloads UI
Render the Readarr downloads table with the cancel download prompt and
the update downloads prompt, and dispatch it from ReadarrUi so the tab
is reachable.

The snapshot suite covers every block in DOWNLOADS_BLOCKS plus loading
and empty states. A second set of snapshots seeds a two row fixture and
selects the second row. Its rows deliberately have different completion
ratios, so that inverting the percentage calculation is caught rather
than cancelling out against a half finished download.
2026-09-04 00:39:33 -06:00
Dark-Alex-17 f73959b73f feat(readarr): add the blocklist UI
Render the Readarr blocklist table with the item details popup, the
remove item prompt and the clear blocklist prompt, and dispatch it from
ReadarrUi so the tab is reachable.

The snapshot suite covers every block in BLOCKLIST_BLOCKS plus loading
and empty states. A second set of snapshots seeds a two row fixture with
distinct values in every column and selects the second row, so that
reading items[0] instead of the current selection cannot pass.
2026-09-04 00:34:46 -06:00
Dark-Alex-17 bad43d1383 feat(readarr): add the history handler 2026-09-04 00:22:04 -06:00
Dark-Alex-17 adf0896b7d feat(readarr): add the downloads handler 2026-09-04 00:07:33 -06:00
Dark-Alex-17 bd96c4ab23 feat(readarr): add the blocklist handler 2026-09-03 23:54:49 -06:00
Dark-Alex-17 e9d3851db8 fix(readarr): stop a long author overview hiding the other fields
The overview was rendered second of twelve into a fixed height block
with no scrolling, so free text of any length pushed status, genres,
rating, path and the statistics off the bottom where they could not be
reached. Every author in a real library has an overview and the median
runs several lines, so those fields were usually invisible.

Rendering it last keeps the structured fields on screen and lets the
overview take whatever space is left.
2026-09-03 22:05:11 -06:00
Dark-Alex-17 773719dbd2 feat(readarr): add the delete book UI 2026-09-03 21:58:35 -06:00
Dark-Alex-17 2b07ad96f1 feat(readarr): add the delete author UI 2026-09-03 21:51:21 -06:00
Dark-Alex-17 7fa091cb44 feat(readarr): add the edit author UI
Renders the edit author prompt with a snapshot per step of the
selection state machine, seeding distinct quality and metadata profile
values so a swap between the two dropdowns cannot go unnoticed.
2026-09-03 21:38:37 -06:00