livello dati C++ (datastore JSON, 33 controlli) e schermate Home, scheda ordine e cover; .gitignore per gli artefatti
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Compila e gira il banco di prova del livello dati con il Qt locale
|
||||
# (il device e' Qt 5.6: qui serve solo verificare la logica di persistenza).
|
||||
# sh tests/cpp_test.sh
|
||||
# Non tocca la configurazione vera: usa una XDG_CONFIG_HOME temporanea.
|
||||
set -e
|
||||
cd "$(dirname "$0")/harness"
|
||||
|
||||
QTDIR=${QTDIR:-/home/kaneda/Qt/5.15.2/gcc_64}
|
||||
PATH="$QTDIR/bin:$PATH"
|
||||
LD_LIBRARY_PATH="$QTDIR/lib:$LD_LIBRARY_PATH"
|
||||
export PATH LD_LIBRARY_PATH
|
||||
|
||||
WORK=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
XDG_CONFIG_HOME="$WORK/config"
|
||||
XDG_DATA_HOME="$WORK/data"
|
||||
export XDG_CONFIG_HOME XDG_DATA_HOME
|
||||
|
||||
rm -f Makefile.harness harness
|
||||
qmake harness.pro -o Makefile.harness >/dev/null
|
||||
make -s -f Makefile.harness
|
||||
./harness
|
||||
@@ -115,6 +115,10 @@ check("stato: fra N giorni", HF.statusKey(4), "in_days");
|
||||
check("dayDiff ignora l'ora", HF.dayDiff(new Date(2026, 3, 27, 23, 59), new Date(2026, 3, 28, 0, 1)), 1);
|
||||
check("parseIso -> solo il giorno", HF.isoDay(HF.parseIso("2026-04-28T09:30:00.000")), "2026-04-28");
|
||||
check("isoDay con padding", HF.isoDay(new Date(2026, 0, 5)), "2026-01-05");
|
||||
check("formatDay gg/mm/aaaa (come la card Android)", HF.formatDay("2026-04-28"), "28/04/2026");
|
||||
check("formatDay su stringa vuota", HF.formatDay(""), "");
|
||||
check("nowIso in formato ISO", /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.000$/.test(HF.nowIso()), true);
|
||||
check("newId unico", HF.newId() !== HF.newId(), true);
|
||||
|
||||
console.log(failures === 0 ? "\ntutti i controlli superati" : "\n" + failures + " controlli falliti");
|
||||
process.exit(failures === 0 ? 0 : 1);
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
// Banco di prova del livello dati (fuori dal pacchetto RPM).
|
||||
// Compila con Qt 5.15 locale e gira con XDG_CONFIG_HOME su una cartella
|
||||
// temporanea, cosi' NON tocca la configurazione vera:
|
||||
// sh tests/cpp_test.sh
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QStandardPaths>
|
||||
#include <QVariantList>
|
||||
#include <QVariantMap>
|
||||
#include <QDebug>
|
||||
|
||||
#include "../../src/datastore.h"
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void check(const QString &name, bool ok, const QString &detail = QString())
|
||||
{
|
||||
if (!ok)
|
||||
failures++;
|
||||
qInfo().noquote() << (ok ? "OK " : "FAIL") << name
|
||||
<< (ok || detail.isEmpty() ? QString() : "-> " + detail);
|
||||
}
|
||||
|
||||
static QVariantList makeBreeds()
|
||||
{
|
||||
QVariantList hens;
|
||||
QVariantMap m1;
|
||||
m1.insert("id", "m1");
|
||||
m1.insert("name", "Marta");
|
||||
m1.insert("birthDate", "2025-01-01T00:00:00.000");
|
||||
m1.insert("isLaying", true);
|
||||
hens.append(m1);
|
||||
|
||||
QVariantMap marans;
|
||||
marans.insert("breedId", "marans");
|
||||
marans.insert("hens", hens);
|
||||
return QVariantList() << marans;
|
||||
}
|
||||
|
||||
static QVariantList makeReservations()
|
||||
{
|
||||
QVariantMap r;
|
||||
r.insert("id", "r1");
|
||||
r.insert("customerName", "Cli1");
|
||||
r.insert("breedId", "araucana");
|
||||
r.insert("eggsRequested", 2);
|
||||
r.insert("createdAt", "2026-04-25T09:00:00.000");
|
||||
r.insert("deliveryDate", "2026-04-28");
|
||||
r.insert("isDelivered", false);
|
||||
return QVariantList() << r;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setOrganizationName("harbour");
|
||||
app.setApplicationName("henflow");
|
||||
|
||||
qInfo().noquote() << "config dir:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
|
||||
// --- primo avvio: nessun file, stato vuoto -----------------------------
|
||||
{
|
||||
DataStore store;
|
||||
check("primo avvio: nessuna razza", store.breedSettings().isEmpty());
|
||||
check("primo avvio: nessuna prenotazione", store.reservations().isEmpty());
|
||||
check("primo avvio: magazzino vuoto", store.warehouseStock().isEmpty());
|
||||
check("primo avvio: nessuna data di aggiornamento", store.lastProductionUpdate().isEmpty());
|
||||
check("il file dati non esiste ancora", !QFile::exists(store.dataFilePath()));
|
||||
}
|
||||
|
||||
// --- scrittura e rilettura --------------------------------------------
|
||||
DataStore store;
|
||||
check("salvataggio razze", store.saveBreedSettings(makeBreeds()));
|
||||
check("salvataggio prenotazioni", store.saveReservations(makeReservations()));
|
||||
|
||||
QVariantMap customers;
|
||||
customers.insert("id", "c1");
|
||||
customers.insert("firstName", "Anna");
|
||||
customers.insert("lastName", "Rossi");
|
||||
customers.insert("phoneNumber", "+39 333 0000000");
|
||||
check("salvataggio clienti", store.saveCustomers(QVariantList() << customers));
|
||||
|
||||
QVariantMap stock;
|
||||
stock.insert("marans", 6.0);
|
||||
stock.insert("araucana", 1.5);
|
||||
check("salvataggio magazzino", store.saveWarehouseStock(stock));
|
||||
check("salvataggio data aggiornamento", store.saveLastProductionUpdate("2026-10-09"));
|
||||
|
||||
check("il file dati esiste", QFile::exists(store.dataFilePath()));
|
||||
|
||||
{
|
||||
DataStore reopened;
|
||||
check("rilettura razze", reopened.breedSettings().size() == 1);
|
||||
check("rilettura nidificata (galline)", reopened.breedSettings().at(0).toMap()
|
||||
.value("hens").toList().size() == 1);
|
||||
check("rilettura prenotazioni", reopened.reservations().size() == 1);
|
||||
check("rilettura quantita' uova", reopened.reservations().at(0).toMap()
|
||||
.value("eggsRequested").toInt() == 2);
|
||||
check("rilettura flag consegna (bool)", reopened.reservations().at(0).toMap()
|
||||
.value("isDelivered").toBool() == false);
|
||||
check("rilettura clienti", reopened.customers().size() == 1 &&
|
||||
reopened.customers().at(0).toMap().value("lastName").toString() == "Rossi");
|
||||
check("rilettura magazzino (numeri)", qFuzzyCompare(reopened.warehouseStock()
|
||||
.value("araucana").toDouble(), 1.5));
|
||||
check("rilettura data aggiornamento", reopened.lastProductionUpdate() == "2026-10-09");
|
||||
}
|
||||
|
||||
// --- contratto con le chiavi dell'app Flutter --------------------------
|
||||
{
|
||||
QFile f(store.dataFilePath());
|
||||
f.open(QIODevice::ReadOnly);
|
||||
const QJsonObject root = QJsonDocument::fromJson(f.readAll()).object();
|
||||
f.close();
|
||||
check("chiave breed_settings_v2 (compatibile con l'app Android)",
|
||||
root.contains("breed_settings_v2"));
|
||||
check("chiave reservations_v2", root.contains("reservations_v2"));
|
||||
check("chiave customers_v1", root.contains("customers_v1"));
|
||||
check("chiave warehouse_stock_v1", root.contains("warehouse_stock_v1"));
|
||||
check("chiave last_production_update_v1", root.contains("last_production_update_v1"));
|
||||
}
|
||||
|
||||
// --- backup / ripristino ----------------------------------------------
|
||||
{
|
||||
const QString backup = QDir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation))
|
||||
.filePath("backup-test.json");
|
||||
check("export", store.exportTo(backup));
|
||||
check("clearAll", store.clearAll());
|
||||
check("dopo clearAll: vuoto", store.reservations().isEmpty() && store.customers().isEmpty());
|
||||
check("import", store.importFrom(backup));
|
||||
check("dopo import: prenotazioni ripristinate", store.reservations().size() == 1);
|
||||
check("dopo import: clienti ripristinati", store.customers().size() == 1);
|
||||
QFile::remove(backup);
|
||||
}
|
||||
|
||||
// --- file corrotto: non deve crashare e non deve perdere i dati ---------
|
||||
{
|
||||
QFile f(store.dataFilePath());
|
||||
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
f.write("{ questo non e' json");
|
||||
f.close();
|
||||
|
||||
DataStore broken;
|
||||
check("file corrotto: non crasha", true);
|
||||
check("file corrotto: stato vuoto", broken.reservations().isEmpty());
|
||||
|
||||
// il backup di un file corrotto viene rifiutato
|
||||
check("import di un file non valido rifiutato", !broken.importFrom(store.dataFilePath()));
|
||||
}
|
||||
|
||||
qInfo().noquote() << (failures == 0 ? "\ntutti i controlli superati"
|
||||
: QString("\n%1 controlli falliti").arg(failures));
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
TEMPLATE = app
|
||||
TARGET = harness
|
||||
CONFIG += console c++11
|
||||
CONFIG -= app_bundle
|
||||
|
||||
# Solo QtCore: il livello dati non tocca la GUI, cosi' il banco di prova
|
||||
# compila su desktop senza QtQuick/Silica.
|
||||
QT += core
|
||||
QT -= gui
|
||||
|
||||
SOURCES += \
|
||||
harness.cpp \
|
||||
../../src/datastore.cpp
|
||||
|
||||
HEADERS += \
|
||||
../../src/datastore.h
|
||||
@@ -53,6 +53,12 @@ check("t() con array", I18N.t("share_order_msg", ["Marans", "2", "28/04/2026"],
|
||||
check("t() lingua sconosciuta -> inglese", I18N.t("new_order", null, "xx"), S.en.new_order);
|
||||
check("t() chiave sconosciuta -> chiave", I18N.t("chiave_inesistente", null, "it"), "chiave_inesistente");
|
||||
|
||||
// lingua effettiva: 'system' segue il telefono
|
||||
check("langFor: scelta esplicita", I18N.langFor("fr", "it_IT"), "fr");
|
||||
check("langFor: system -> lingua del telefono", I18N.langFor("system", "de_DE"), "de");
|
||||
check("langFor: system non supportata -> inglese", I18N.langFor("system", "es_ES"), "en");
|
||||
check("langFor: scelta non valida -> inglese", I18N.langFor("xx", "it_IT"), "en");
|
||||
|
||||
// il testo con a capo e' una vera nuova riga (in main.dart e' '\n')
|
||||
check("newline reale (no_breeds_sel)",
|
||||
S.it.no_breeds_sel.indexOf("\n") > 0, true);
|
||||
|
||||
Reference in New Issue
Block a user