feat(auto): surface favorite groups as Android Auto sub-folders
Favoritos now renders non-empty custom groups as grupo:<id> sub-folders (hidden when empty) with ungrouped stations left as direct leaves, reusing the existing hijos() path so the zero-groups case stays byte-identical to today's flat list.
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# Verify Report: android-auto-favorite-groups
|
||||
|
||||
**Verdict**: PASS WITH WARNINGS
|
||||
**Mode**: Strict TDD verify (fresh adversarial pass against live code)
|
||||
**Date**: 2026-07-19
|
||||
|
||||
## Test Execution Evidence
|
||||
|
||||
Command actually run (targeted, not full suite, per environment constraint):
|
||||
```
|
||||
flutter test test/servicios/navegacion_auto_test.dart test/estado/estado_radio_test.dart --concurrency=1 --timeout=60s
|
||||
```
|
||||
Result: All tests passed! 54/54 total.
|
||||
- test/servicios/navegacion_auto_test.dart: 33/33 passed (verified by both live run indices +0..+32 and grep -c count of test() calls = 33)
|
||||
- test/estado/estado_radio_test.dart: 21/21 passed (indices +33..+53, grep -c count = 21)
|
||||
|
||||
Discrepancy vs apply-progress claim: apply-progress.md states 55/55 (34/34 navegacion_auto_test.dart, 21/21 estado_radio_test.dart). Actual is 54/54 (33/33 + 21/21). Functionally harmless (every test that exists passes), but the reported count is off by one test in navegacion_auto_test.dart. WARNING, not CRITICAL: no missing coverage was found, no scenario is untested, this is a report-accuracy defect only.
|
||||
|
||||
## Working Tree State
|
||||
|
||||
git status --porcelain confirms only unstaged modifications plus one untracked dir, nothing committed:
|
||||
```
|
||||
M lib/estado/estado_radio.dart
|
||||
M lib/servicios/navegacion_auto.dart
|
||||
M lib/servicios/servicio_audio.dart
|
||||
M test/estado/estado_radio_test.dart
|
||||
M test/servicios/navegacion_auto_test.dart
|
||||
?? openspec/changes/android-auto-favorite-groups/
|
||||
```
|
||||
|
||||
git diff --stat on the 5 changed files: 290 insertions(+), 1 deletion(-) = 291 changed lines, matching apply-progress's 291-changed-lines claim exactly, and within the tasks.md forecast (280-370, Medium risk, no chaining needed).
|
||||
|
||||
## Spec Compliance Matrix
|
||||
|
||||
| Spec Requirement / Scenario | Status | Evidence |
|
||||
|---|---|---|
|
||||
| Favorite Group Sub-Folders - groups exist under Favoritos | PASS | carpetasFavoritos test "con un grupo personalizado no vacio" (navegacion_auto_test.dart:459-482) |
|
||||
| grupo:<id> scheme distinct from emisora:<uuid> | PASS | esCarpetaGrupo/resolver both prefix-gated; collision test at line 557-565 (resolver("grupo:g1", ...) returns null) |
|
||||
| Group folder stations, sorted+capped, playable via existing path | PASS | hijosGrupo reuses ordenarEmisoras + _maxItemsPorCarpeta (50); test at 527-554 |
|
||||
| Unknown/stale group id returns empty list, not error | PASS | hijosGrupo early-returns const [] on !esCarpetaGrupo, empty id, or empty miembros; tested (line 549-553) |
|
||||
| Empty Favorite Group Handling - omitted from tree | PASS | carpetasFavoritos filters favoritos.any((e) => e.grupoFavoritosId == g.id); real empty-group fixture test "un grupo personalizado vacio se omite" (484-505) - genuine fixture (zero matching favorites), not an assertion-by-name only |
|
||||
| No user-facing dead end (empty group browsed) | PASS (by construction) | Empty groups never surfaced as folders (prior row), so the "browsed empty folder" scenario collapses to N/A by design; hijosGrupo also independently returns [] for any unmatched id without throwing |
|
||||
| Browsable Media Tree (MODIFIED) - root unaffected | PASS | raiz() unchanged, still 3 folders; no test regressions |
|
||||
| Ungrouped station appears exactly as before (regression guard) | PASS, verified via direct source trace | carpetasFavoritos literally calls hijos(idFavoritos, emisoras: sinAsignar) (navegacion_auto.dart:234) - the same function/call used pre-change, not a parallel reimplementation. Confirmed by reading the function body, not by trusting the test name. Regression test (429-457) additionally asserts id/playable/title equality against hijos()'s own output for the no-custom-groups case. |
|
||||
| _maxGruposPorFavoritos = 50 cap enforced | PASS | carpetasFavoritos uses .take(_maxGruposPorFavoritos); test with 60 eligible groups asserts exactly 50 folders returned (507-524). Not an exact-boundary (50 vs 51) test, follows the same bulk-of-60 style as the pre-existing hijos 50-cap test (precedent), so treated as SUGGESTION not a gap. |
|
||||
| Ordering follows orden ASC, nombre ASC | PASS | ServicioFavoritos.obtenerGrupos() SQL: orderBy: "orden ASC, nombre ASC" (servicio_favoritos.dart:166-172); carpetasFavoritos/itemGrupo apply no re-sort, only filter+take+map, phone order preserved by construction |
|
||||
|
||||
## Design Coherence
|
||||
|
||||
| Design Decision | Implemented? |
|
||||
|---|---|
|
||||
| Empty groups hidden from car tree | Yes - filter condition confirmed above |
|
||||
| Ungrouped favorites stay as direct leaves, never own folder | Yes - esSinAsignar filtered out of carpetas; sin_asignar leaves appended via hijos() |
|
||||
| grupo:<id> media-id scheme | Yes |
|
||||
| Separate _maxGruposPorFavoritos constant (not reusing _maxItemsPorCarpeta) | Yes - declared and used independently |
|
||||
| FuenteEmisorasAuto.grupos() cold-start-safe (try/catch -> []) | Yes - FuenteEmisorasAutoLocal.grupos() mirrors favoritos()'s try/catch pattern exactly |
|
||||
| EstadoRadio.cargarGruposFavoritos() pushes snapshot | Yes - _fuenteAuto?.actualizarSnapshot(grupos: _gruposFavoritos) added, mirrors cargarFavoritos()'s pattern |
|
||||
| Handler dispatch stays thin (2 new branches only) | Yes - servicio_audio.dart getChildren adds exactly 2 branches, delegates all logic to pure ConstructorArbolAuto methods |
|
||||
|
||||
## Undocumented Fix Verification (apply-progress "Issues Found")
|
||||
|
||||
Claim: test/estado/estado_radio_test.dart's _FuenteEmisorasAutoEspia needed grupos() added to keep compiling, and a real assertion was added (not just a compile stub).
|
||||
|
||||
Confirmed real: the spy implements grupos() returning ultimoGrupos ?? const [], records pushes via actualizarSnapshot, and the test at line 588-591 asserts fuenteAuto.ultimoGrupos?.map((g) => g.id) contains GrupoFavoritos.sinAsignarId after estado.inicializar() - a genuine behavioral assertion on the new grupos: push, not a no-op compile fix.
|
||||
|
||||
## Task Completion
|
||||
|
||||
All 22 tasks in tasks.md are marked [x] and match the code state - verified by direct inspection of the described changes in each of the 5 changed files, not by trusting the checkbox alone.
|
||||
|
||||
## Encoding / Literal Scan
|
||||
|
||||
Ran a targeted scan of the diff (git diff on the 5 changed files) for mojibake markers (Latin-1-as-UTF-8 sequences, stray replacement char). No hits. Spanish comments and identifiers in the diff are clean UTF-8.
|
||||
|
||||
## Issues Found
|
||||
|
||||
### CRITICAL
|
||||
None.
|
||||
|
||||
### WARNING
|
||||
1. Test-count report accuracy: apply-progress.md claims 55/55 (34/34 navegacion_auto_test.dart, 21/21 estado_radio_test.dart). Actual, independently re-run and grep-verified: 54/54 (33/33 + 21/21). No missing test/coverage - purely a documentation-accuracy defect in the apply report. Should be corrected before archive so the archived record is accurate.
|
||||
|
||||
### SUGGESTION
|
||||
1. The _maxGruposPorFavoritos = 50 cap test uses 60 groups (bulk-over-cap), not an exact 50-vs-51 boundary case. Consistent with the pre-existing hijos 50-cap test's own style (same bulk-of-60 precedent), so low priority - but a tighter boundary test would be marginally more rigorous.
|
||||
2. Design's "Open Questions" item (mixed browsable+playable children rendering acceptably on real car UI) remains unresolved by nature - requires DHU/real-hardware testing, correctly flagged as manual follow-up in both design.md and tasks.md 4.3. Not blocking; carry forward as a known residual risk until manually verified on hardware.
|
||||
|
||||
## Final Verdict
|
||||
|
||||
PASS WITH WARNINGS - implementation matches spec and design, the most important regression guard (byte-identical Favoritos folder rendering with zero custom groups) is genuinely satisfied via direct code reuse (not a parallel reimplementation), all 54 targeted tests pass under a fresh independent run, working tree is uncommitted as expected, and diff size matches the reported forecast. The only defect found is a cosmetic test-count inaccuracy in apply-progress.md (54 actual vs. 55 claimed) - recommend correcting that number before archive, but it does not block archival of the functional change.
|
||||
Reference in New Issue
Block a user