diff --git a/CLAUDE.md b/CLAUDE.md index b3fdad3..6842bb7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,6 +47,7 @@ bash docker/build.sh --release # release → dist/biteplan-release. - **Emulatore**: Android SDK in `/home/davide/android-sdk`; accelerazione hardware via `/dev/kvm` (WSL2 con KVM abilitato). - **AVD `biteplan`**: dedicato a questo progetto (Pixel 6, Android 14, google_apis/x86_64). Non riutilizzare AVD di altri progetti (es. `palladium_wallet`). - **Permessi**: le build Docker girano come root nel volume montato — se `.dart_tool/` o `build/` risultano di proprietà root, serve `sudo chown -R davide:davide .dart_tool build` prima di usare flutter da host. +- **Setup e uso dell'emulatore Android** (installazione SDK, creazione AVD, comandi `adb`, troubleshooting): [docs/emulatore.md](docs/emulatore.md). ## Build e firma @@ -88,7 +89,7 @@ lib/ ## Testing -184 test unit + widget in `test/` (rispecchia la struttura di `lib/`), più un integration test in `integration_test/app_test.dart`. I test unit/widget non richiedono device. Coverage ~93% su `lib/` (esclusi per scelta: `main.dart`, il percorso camera di `qr_scan_page`). +184 test unit + widget in `test/` (rispecchia la struttura di `lib/`), più un integration test in `integration_test/app_test.dart`. I test unit/widget non richiedono device. Coverage ~93% su `lib/` (esclusi per scelta: `main.dart`, il percorso camera di `qr_scan_page`). Dettaglio di cosa è coperto/escluso e come lanciare la suite: [docs/testing.md](docs/testing.md). - Storage isolato con `SharedPreferences.setMockInitialValues({})` - Widget test: estensione `pumpApp` in `test/helpers/pump_app.dart` diff --git a/README.md b/README.md index 0e0209a..2400bb9 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,8 @@ adb uninstall com.davide.biteplan ## Test +Cosa è coperto, cosa no e insidie note: [docs/testing.md](docs/testing.md). + ```bash flutter test # tutta la suite (host) flutter test test/features/meal_planner/qr_test.dart # singolo file @@ -227,6 +229,8 @@ per modifiche più ampie apri prima una issue di discussione prima di lavorare a - [Guida utente](docs/guida-utente.md) - [Architettura](docs/architettura.md) - [Fonti e documentazione conversioni](docs/conversioni.md) +- [Emulatore Android — installazione e uso](docs/emulatore.md) +- [Test — cosa è coperto e come lanciarli](docs/testing.md) - [Changelog](CHANGELOG.md) ## Stato e licenza diff --git a/docs/emulatore.md b/docs/emulatore.md new file mode 100644 index 0000000..b764713 --- /dev/null +++ b/docs/emulatore.md @@ -0,0 +1,121 @@ +# Emulatore Android — installazione e uso (Ubuntu) + +Guida per chi sviluppa BitePlan su Ubuntu (nativo o WSL2) e deve installare ed usare +l'emulatore Android in locale. Per i requisiti generali del progetto vedi [README](../README.md); +per la build via Docker vedi [CLAUDE.md](../CLAUDE.md). + +## 1. Prerequisiti + +- **KVM** per l'accelerazione hardware: senza, l'emulatore è troppo lento per essere usabile. + + ```bash + ls /dev/kvm # deve esistere + groups | grep kvm # il tuo utente deve essere nel gruppo kvm + ``` + + Se il gruppo manca: + + ```bash + sudo usermod -aG kvm $USER + # poi rilogin (o riavvia la sessione) perché il gruppo sia effettivo + ``` + + Su **WSL2** serve un kernel con KVM abilitato (le versioni recenti lo hanno di serie). + La finestra dell'emulatore compare tramite WSLg. + +- **Java 17+**: `sudo apt install openjdk-21-jdk` +- Strumenti di base: `git curl unzip`, e ~15 GB di spazio libero per SDK + system image. + +## 2. Installare Android SDK command-line tools + +```bash +mkdir -p ~/android-sdk/cmdline-tools +cd ~/android-sdk/cmdline-tools +curl -O https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip +unzip commandlinetools-linux-11076708_latest.zip && mv cmdline-tools latest +rm commandlinetools-linux-11076708_latest.zip + +cat >> ~/.bashrc <<'EOF' +export ANDROID_HOME="$HOME/android-sdk" +export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH" +EOF +source ~/.bashrc +``` + +## 3. Installare i pacchetti SDK e accettare le licenze + +```bash +sdkmanager "platform-tools" "emulator" \ + "platforms;android-34" "build-tools;34.0.0" \ + "system-images;android-34;google_apis;x86_64" +yes | sdkmanager --licenses +``` + +## 4. Creare l'AVD `biteplan` + +Il progetto usa un AVD dedicato (Pixel 6, Android 14, `google_apis`/x86_64) — non riusare +AVD di altri progetti. + +```bash +echo "no" | avdmanager create avd -n biteplan \ + -k "system-images;android-34;google_apis;x86_64" -d pixel_6 +``` + +Verifica che tutto sia a posto con `flutter doctor` (la voce "Android toolchain" deve +risultare ✓). + +## 5. Avviare l'emulatore + +```bash +emulator -avd biteplan & +``` + +Il primo boot richiede 1-2 minuti; quelli successivi sono più veloci grazie allo snapshot +automatico dello stato. Per lo sviluppo con hot reload, dalla root del progetto: + +```bash +flutter run # r = hot reload, R = hot restart +``` + +### Opzioni utili + +```bash +emulator -list-avds # elenca gli AVD disponibili +emulator -avd biteplan -no-snapshot-load # forza un cold boot (ignora lo stato salvato) +emulator -avd biteplan -wipe-data # resetta l'AVD allo stato di fabbrica +``` + +## 6. Comandi `adb` quotidiani + +```bash +adb devices # emulatori/device collegati +adb wait-for-device # attende che l'emulatore sia raggiungibile +adb shell 'while [ "$(getprop sys.boot_completed)" != "1" ]; do sleep 1; done' # attende il boot completo + +adb install -r dist/biteplan-debug.apk # installa/reinstalla un APK già buildato +adb shell monkey -p com.davide.biteplan -c android.intent.category.LAUNCHER 1 # avvia l'app + +adb logcat --pid=$(adb shell pidof -s com.davide.biteplan) # log in tempo reale dell'app + +adb uninstall com.davide.biteplan # disinstalla +``` + +> Per lo sviluppo iterativo usa `flutter run` (hot reload); la sequenza `adb install` sopra +> serve a validare l'APK esatto che verrà distribuito, senza passare da `flutter run`. + +## 7. Chiudere l'emulatore + +```bash +adb -s emulator-5554 emu kill # spegnimento pulito (salva lo snapshot) +# oppure chiudi la finestra dell'emulatore +``` + +## Troubleshooting + +| Sintomo | Causa probabile | Soluzione | +|---|---|---| +| `emulator: ERROR: x86_64 emulation currently requires hardware acceleration` | KVM non accessibile | Verifica `/dev/kvm` e l'appartenenza al gruppo `kvm` (§1) | +| Emulatore lentissimo, UI a scatti | KVM non attivo, o system image ARM su host x86_64 | Usa una system image `x86_64` come da §3, verifica KVM | +| Boot bloccato su logo Android per minuti | Cold boot dopo `-wipe-data`, oppure macchina sotto carico | Attendi, è normale al primo avvio; se persiste prova `-no-snapshot-load` | +| Finestra emulatore non compare su WSL2 | WSLg non attivo o kernel senza supporto grafico | Aggiorna WSL (`wsl --update`), verifica `echo $DISPLAY` | +| `adb: no devices/emulators found` | Emulatore non ancora avviato o non ancora bootato | `adb wait-for-device`, poi ricontrolla con `adb devices` | diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000..9c44229 --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,78 @@ +# Test — BitePlan + +Cosa copre la suite di test, cosa no, e come lanciarla. Per l'architettura del codice +testato vedi [architettura.md](architettura.md). + +## Come lanciare i test + +```bash +flutter test # tutta la suite unit + widget (host) +flutter test test/features/meal_planner/qr_test.dart # singolo file + +# versione riproducibile via Docker (immagine biteplan-build, creata da bash docker/build.sh) +docker run --rm -v "$(pwd):/workspace" -w /workspace biteplan-build \ + bash -c "flutter pub get --enforce-lockfile && flutter test" + +# integration test — richiede un emulatore avviato (vedi docs/emulatore.md) +flutter test integration_test/app_test.dart +``` + +## Struttura + +```text +test/ +├── coverage_helper_test.dart # importa tutta lib/ perché lcov copra anche i file non testati +├── helpers/pump_app.dart # estensione pumpApp per i widget test +├── features/ +│ ├── converter/{models,pages,providers}/ +│ ├── meal_planner/{models,pages,providers,widgets}/ + qr_test.dart +│ ├── shopping_list/{models,pages,providers,widgets}/ +│ └── guide/{pages,widgets}/ +└── shared/{services,widgets}/ +``` + +184 test unit + widget, coverage ~93% su `lib/`, più `integration_test/app_test.dart` +(navigazione tra tab, flussi end-to-end su un emulatore/device reale). + +## Cosa è coperto + +- **Modelli**: serializzazione/deserializzazione, `copyWith`, edge case (`MealPlan`, + `DayPlan`, `ConversionEntry`, `ShoppingItem`). +- **Provider**: mutazioni di stato, persistenza (fire-and-forget) tramite + `StorageService` con `SharedPreferences.setMockInitialValues({})` isolato per test. +- **Conversione crudo/cotto**: `rawToCooked`/`cookedToRaw` in `ConversionEntry`. +- **QR**: `buildQrPayload`/`parseMealPlanFromQr` in `qr_codec.dart` (payload valido, + troppo grande, malformato). +- **Update checker**: `isNewer()`, `parseTagName()` (`@visibleForTesting`), dialog di + aggiornamento con `MethodChannel` mockato. +- **Widget/pagine**: rendering, interazioni utente (tap, form) via `pumpApp`, per tutte + le feature (meal_planner, converter, shopping_list, guide). +- **Integration test**: navigazione fra i tab e flussi che attraversano più feature, + su un device/emulatore reale (non richiesto per lo sviluppo quotidiano). + +## Cosa NON è coperto (per scelta) + +- **`main.dart`**: bootstrap dell'app (setup provider, `runApp`), poco valore nel + testarlo isolatamente — è coperto indirettamente dall'integration test. +- **Percorso camera di `qr_scan_page`**: l'uso reale della fotocamera per scansionare un + QR richiede hardware/permessi non simulabili in un widget test; la logica di parsing + (`qr_codec.dart`) è invece testata a parte. +- **`UrlLauncherService`**: wrapper sottile sul `MethodChannel` nativo + (`com.davide.biteplan/launcher`) che apre un URL nel browser — l'effetto (apertura + browser) non è verificabile senza un device reale. + +## Insidie note + +- **Non chiamare `loadDb()`/`load()` dei provider nel body di `testWidgets`**: l'async + reale eseguito nella zona `FakeAsync` di `flutter_test` può bloccare la suite (timeout + 10 min). Vanno chiamati nel `setUp()` — vedi + [test/features/converter/pages/converter_page_test.dart](../test/features/converter/pages/converter_page_test.dart). +- **Storage isolato**: ogni test che tocca `StorageService`/i provider deve chiamare + `SharedPreferences.setMockInitialValues({})` prima di caricare lo stato, altrimenti i + test si influenzano a vicenda. +- **MethodChannel nei test**: va mockato con + `TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler` + — vedi [test/shared/widgets/update_dialog_test.dart](../test/shared/widgets/update_dialog_test.dart). +- **Nuovi file in `lib/`**: aggiungerli all'import di + [test/coverage_helper_test.dart](../test/coverage_helper_test.dart), altrimenti lcov + li esclude dal report di coverage anche se non hanno bisogno di test dedicati.