Files
harbour-aira/tests/core_test.cpp
T
Carlo fddd21e16a 0.4.0: interfaccia in italiano e inglese, scelta nelle impostazioni
L'inglese è la lingua sorgente delle stringhe; l'italiano sta nel catalogo
translations/harbour-aira-it.ts (99 stringhe), che lupdate aggiorna e lrelease
compila in build. Di serie la lingua del sistema, con ripiego sull'inglese.

- nuova classe Translator: risolve la lingua prima di createView e allinea
  QLocale::setDefault, così anche i numeri seguono la lingua (23,0 °C in
  italiano, 23.0 °C in inglese) — le temperature passano da formatValue, non
  più dal toFixed() di JavaScript che metteva sempre il punto
- Qt 5.6 non ha QQmlEngine::retranslate(): al cambio di lingua la radice QML
  viene ricaricata (setSource ricrea il componente anche a url invariata)
- le etichette di stato in airajson.cpp usano QCoreApplication::translate per
  esteso: lupdate non riconosce una funzione wrapper e le avrebbe perse
- selettore della lingua in Impostazioni (Sistema / English / Italiano)
- test: 41/41, carica il .qm vero come fa main.cpp e verifica entrambe le
  lingue, il ripiego e i formati numerici
- anteprime bilingui (docs/anteprima/ inglese, it/ italiano), README e PIANO
  aggiornati, spec RPM con i cataloghi in %files
2026-10-10 08:19:20 +02:00

155 lines
8.2 KiB
C++

// Verifica locale del nucleo di harbour-aira, senza SDK Sailfish.
// Copre la parte pura (src/airajson.cpp): normalizzazione dell'indirizzo,
// lettura del riassunto, etichette di stato, corpo dei comandi, messaggi
// d'errore. Il networking e la UI si verificano sul device.
//
// Build: ~/Qt/5.15.2/gcc_64/bin/qmake tests/core_test.pro && make && ./core_test
#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>
#include <QLocale>
#include <QStringList>
#include <QTranslator>
#include "../src/airajson.h"
static int failures = 0;
static int checks = 0;
static void check(bool cond, const QString &what)
{
++checks;
if (!cond) {
++failures;
qWarning() << "FALLITO:" << what;
} else {
qInfo() << "ok:" << what;
}
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
// ---- normalizeBaseUrl ------------------------------------------------- //
check(AiraJson::normalizeBaseUrl("192.168.2.50:8790") == "http://192.168.2.50:8790",
"host:porta riceve lo schema http");
check(AiraJson::normalizeBaseUrl("http://192.168.2.50:8790") == "http://192.168.2.50:8790",
"schema http preservato");
check(AiraJson::normalizeBaseUrl("https://aira.example.tld/bridge/") == "https://aira.example.tld/bridge",
"https preservato, slash finale tolto");
check(AiraJson::normalizeBaseUrl(" 192.168.2.50:8790// ") == "http://192.168.2.50:8790",
"spazi e slash ripetuti normalizzati");
check(AiraJson::normalizeBaseUrl("").isEmpty(), "stringa vuota resta vuota (nessuna richiesta)");
check(AiraJson::normalizeBaseUrl(" ").isEmpty(), "solo spazi = vuoto");
// ---- parseSummary ----------------------------------------------------- //
const QByteArray good = R"({"ok":true,"summary":{"outdoor_temperature":18.1,"dhw_current":51.0,"dhw_target":55.0,"operating_status":"OPERATING_STATUS_AUTO","away_mode":false,"errors_count":0}})";
bool ok = false;
QVariantMap m = AiraJson::parseSummary(good, &ok);
check(ok, "riassunto valido riconosciuto");
check(qFuzzyCompare(m.value("dhw_current").toDouble(), 51.0), "dhw_current letto");
check(m.value("operating_status").toString() == "OPERATING_STATUS_AUTO", "operating_status letto");
QVariantMap bad = AiraJson::parseSummary("non sono json", &ok);
check(!ok && bad.isEmpty(), "JSON non valido -> vuoto con ok=false");
QVariantMap err = AiraJson::parseSummary(R"({"ok":false,"error":"token errato"})", &ok);
check(!ok && err.isEmpty(), "risposta di errore -> vuoto");
check(AiraJson::parseSummary(R"({"ok":true})", &ok).isEmpty() && !ok,
"ok=true senza summary -> vuoto");
// ---- modeLabel -------------------------------------------------------- //
// Attese in inglese: e' la lingua SORGENTE delle stringhe (l'italiano arriva
// dal catalogo caricato piu' avanti).
check(AiraJson::modeLabel("OPERATING_STATUS_AUTO") == "Automatic", "AUTO -> Automatic");
check(AiraJson::modeLabel("OPERATING_STATUS_HOT_WATER") == "Hot water", "hot water");
check(AiraJson::modeLabel("") == "—", "stato vuoto -> trattino");
check(AiraJson::modeLabel("PUMP_MODE_STATE_HEATING") == "Heating",
"zone state: heating");
check(AiraJson::modeLabel("PUMP_MODE_STATE_IDLE") == "Idle",
"zone state: idle");
check(AiraJson::modeLabel("PUMP_MODE_STATE_IGNOTO") == "ignoto",
"unknown zone state -> prefix stripped");
check(AiraJson::modeLabel("PUMP_ACTIVE_STATE_IDLE") == "Stopped",
"pump stopped");
check(AiraJson::modeLabel("PUMP_ACTIVE_STATE_RUNNING") == "Running",
"pump running");
check(AiraJson::modeLabel("OPERATING_STATUS_DEFROST") == "defrost",
"unknown state: prefix stripped, lowercase");
// ---- formatValue ------------------------------------------------------ //
// Il separatore decimale segue il locale predefinito (che translator.cpp
// allinea alla lingua scelta): qui si fissa per rendere l'esito deterministico
// e si verifica che cambi davvero con la lingua.
QLocale::setDefault(QLocale::c());
check(AiraJson::formatValue(QVariant(51.0)) == "51.0", "numero a un decimale (locale C)");
check(AiraJson::formatValue(QVariant(18.14)) == "18.1", "arrotondamento a un decimale");
QLocale::setDefault(QLocale(QLocale::Italian, QLocale::Italy));
check(AiraJson::formatValue(QVariant(23.0)) == "23,0", "in italiano separatore virgola");
check(AiraJson::formatValue(QVariant(18.14)) == "18,1", "in italiano arrotonda con la virgola");
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedKingdom));
check(AiraJson::formatValue(QVariant(23.0)) == "23.0", "in inglese separatore punto");
QLocale::setDefault(QLocale::c());
// Invariante su cui poggia Translator::apply(""): QLocale::system() non risente
// di setDefault(), quindi "lingua vuota" torna sempre alla lingua del telefono
// anche dopo che l'utente ha scelto l'italiano. (QLocale() invece cambierebbe.)
const QLocale systemBefore = QLocale::system();
QLocale::setDefault(QLocale(QLocale::Italian, QLocale::Italy));
check(QLocale::system() == systemBefore, "setDefault non tocca QLocale::system()");
check(QLocale() == QLocale(QLocale::Italian, QLocale::Italy), "QLocale() segue setDefault");
QLocale::setDefault(QLocale::c());
check(AiraJson::formatValue(QVariant()) == "—", "valore assente -> trattino");
check(AiraJson::formatValue(QVariant(QString())) == "—", "stringa vuota -> trattino");
// ---- commandPayload --------------------------------------------------- //
const QJsonObject c1 = QJsonDocument::fromJson(
AiraJson::commandPayload("set_hot_water_target", 55)).object();
check(c1.value("id").toString() == "set_hot_water_target", "id nel corpo del comando");
check(c1.value("value").toInt() == 55, "valore numerico nel corpo");
const QJsonObject c2 = QJsonDocument::fromJson(
AiraJson::commandPayload("hot_water_boost_on", QVariant())).object();
check(c2.contains("value") == false, "comando senza parametri: nessun campo value");
// ---- errorMessage ----------------------------------------------------- //
check(AiraJson::errorMessage(R"({"ok":false,"error":"token mancante o errato"})", 401)
== "token mancante o errato", "messaggio d'errore dal corpo");
check(AiraJson::errorMessage("", 502) == "HTTP error 502", "http status fallback");
check(AiraJson::errorMessage("", 0) == "server unreachable", "status 0 = unreachable");
// ---- catalogo italiano: il .qm caricato davvero ------------------------ //
// Non basta che il .ts esista: qui si installa il .qm come fa main.cpp e si
// controlla che le stringhe sorgente inglesi diventino italiane. Il percorso
// si puo' passare come argv[1]; il default e' quello relativo alla cartella
// di build del test (tests/build).
{
const QString qm = (argc > 1) ? QString::fromLocal8Bit(argv[1])
: QStringLiteral("../../translations/harbour-aira-it.qm");
QTranslator translator;
if (translator.load(qm)) {
QCoreApplication::installTranslator(&translator);
check(AiraJson::modeLabel("PUMP_MODE_STATE_HEATING") == "Riscaldamento",
"catalogo it: Heating -> Riscaldamento");
check(AiraJson::modeLabel("PUMP_ACTIVE_STATE_IDLE") == "Ferma",
"catalogo it: Stopped -> Ferma");
check(AiraJson::modeLabel("PUMP_MODE_STATE_IDLE") == "In attesa",
"catalogo it: Idle -> In attesa");
check(AiraJson::errorMessage("", 0) == "server non raggiungibile",
"catalogo it: messaggio d'errore tradotto");
QCoreApplication::removeTranslator(&translator);
check(AiraJson::modeLabel("PUMP_MODE_STATE_HEATING") == "Heating",
"catalogo rimosso: si torna all'inglese (ripiego)");
} else {
qWarning() << "SALTO il controllo del catalogo: .qm non trovato in" << qm
<< "-> lancialo con lrelease, oppure passa il percorso come argomento";
}
}
qInfo() << "----";
qInfo() << checks - failures << "/" << checks << "verifiche superate";
return failures == 0 ? 0 : 1;
}