/* tests/session/driver.cpp * Integrationstest der Anwendung. * * Spielt eine vollstaendige Sitzung durch -- dieselbe Folge von Kommandos, * die ein Benutzer eintippen wuerde. Was hier gruen ist, funktioniert im * Programm. */ #include #include #include #include #include #include #define CHECK(cond) \ do \ { \ if (!(cond)) \ { \ std::fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \ return 1; \ } \ } while (0) namespace fs = std::filesystem; namespace { /* Fuehrt eine Zeile aus und verlangt, dass sie GELINGT. Gibt die Meldung aus, * wenn nicht -- sonst sucht man den Fehler in der falschen Zeile. */ bool run_ok(pda::Shell& shell, const std::string& line) { const pda::CommandResult result = shell.execute(line); if (result.failed) { std::fprintf(stderr, " Kommando '%s' scheiterte: %s\n", line.c_str(), result.output.c_str()); return false; } return true; } int a_full_session_from_empty_to_saved(const fs::path& dir) { pda::Shell shell{dir}; /* Leerer Anfang. */ CHECK(shell.execute("contact list").output == "keine Kontakte"); CHECK(shell.execute("note list").output == "Notizpuffer leer"); /* Kontakte anlegen -- mit Leerzeichen im Namen und in der Notiz. */ CHECK(run_ok(shell, R"(contact add "Anna Schmidt" 0151 anna@example.org meine Schwester)")); CHECK(run_ok(shell, R"(contact add "Bert Meier" 0170 bert@firma.de Kollege)")); CHECK(shell.contacts().size() == 2U); /* Suchen. */ CHECK(shell.execute("contact find schmidt").output.contains("Anna")); CHECK(shell.execute("contact find zzz").output == "nichts gefunden"); /* Rechnen zwischendurch. */ CHECK(shell.execute("calc 12 * 12").output == "144"); /* Notizen. */ CHECK(run_ok(shell, "note add Anna anrufen")); CHECK(run_ok(shell, "note add Rechnung bezahlen")); CHECK(shell.notes().line_count() == 2U); CHECK(run_ok(shell, "note del 1")); CHECK(shell.notes().line_count() == 1U); CHECK(*shell.notes().line(1U) == "Rechnung bezahlen"); /* Sichern. */ const std::string contacts_file = (dir / "kontakte.tsv").string(); const std::string notes_file = (dir / "notizen.txt").string(); CHECK(run_ok(shell, "contact save " + contacts_file)); CHECK(run_ok(shell, "note save " + notes_file)); /* Eine zweite Sitzung muss dasselbe wiederfinden. */ pda::Shell restored{dir}; CHECK(run_ok(restored, "contact load " + contacts_file)); CHECK(run_ok(restored, "note load " + notes_file)); CHECK(restored.contacts().size() == 2U); CHECK(restored.contacts().find("Anna Schmidt")->note == "meine Schwester"); CHECK(restored.notes().line_count() == 1U); /* Der Explorer sieht beide Dateien. */ const pda::CommandResult listing = restored.execute("ls"); CHECK(!listing.failed); CHECK(listing.output.contains("kontakte.tsv")); CHECK(listing.output.contains("notizen.txt")); return 0; } int errors_do_not_corrupt_the_session(const fs::path& dir) { pda::Shell shell{dir}; CHECK(run_ok(shell, "contact add Anna 0151 a@b.c")); /* Eine Reihe von Fehlern -- danach muss der Zustand unveraendert sein. */ const std::vector broken{"fliegen", "calc 1 / 0", "calc )(", "contact add Anna", "note del abc", "cd gibtesnicht", "contact load /nix/da.tsv"}; for (const std::string& line : broken) { const pda::CommandResult result = shell.execute(line); if (!result.failed) { std::fprintf(stderr, " '%s' haette scheitern muessen\n", line.c_str()); return 1; } } CHECK(shell.contacts().size() == 1U); CHECK(shell.notes().empty()); CHECK(shell.files().current() == dir); return 0; } } /* namespace */ int main() { /* main darf nichts nach aussen werfen: eine entwichene Exception waere * ein std::terminate, und CTest zeigte nur "Subprocess aborted" ohne * jeden Hinweis, welcher Fall gescheitert ist. */ try { const fs::path dir = fs::current_path() / "integration.session.tmp"; std::error_code ec; fs::remove_all(dir, ec); fs::create_directories(dir, ec); if (ec) { std::fprintf(stderr, "FAIL: Arbeitsverzeichnis nicht anzulegen\n"); return 1; } int failures = 0; failures += a_full_session_from_empty_to_saved(dir); failures += errors_do_not_corrupt_the_session(dir); fs::remove_all(dir, ec); if (failures != 0) return 1; std::puts("integration/session: ok"); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "FAIL: unerwartete Exception: %s\n", error.what()); return 1; } }