/* tests/basics/driver.cpp * Integrationstest der Bibliothek (P1204R0 Regel 7.2). * * Kein Test-Framework: ein Integrationstest soll ein eigenstaendiges Programm * sein, das ohne Argumente laeuft und ueber seinen Exit-Code Auskunft gibt. * Er benutzt nur -- genau das, was ein Benutzer bekommt. * * Der Test bildet einen kleinen echten Ablauf ab: Kontakte anlegen, sichern, * in einem zweiten Buch laden, Notizen schreiben und ueber den Explorer * nachsehen, dass die Dateien wirklich da sind. */ #include #include #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 { int contacts_survive_a_save_and_load_cycle(const fs::path& dir) { const fs::path file = dir / "kontakte.tsv"; pda::ContactBook book; CHECK(book.add(pda::Contact{"Anna Schmidt", "0151", "anna@example.org", "Schwester"})); CHECK(book.add(pda::Contact{"Bert Meier", "0170", "bert@firma.de", "Kollege\tAbt. 4"})); CHECK(book.size() == 2U); CHECK(book.save(file).has_value()); pda::ContactBook reloaded; CHECK(reloaded.load(file).has_value()); CHECK(reloaded.size() == 2U); const pda::Contact* bert = reloaded.find("Bert Meier"); CHECK(bert != nullptr); /* Das TAB in der Notiz ist der Grund fuer das Escaping in textfile.c -- * ohne das haette der Datensatz beim Laden fuenf Felder statt vier. */ CHECK(bert->note == "Kollege\tAbt. 4"); return 0; } int notes_round_trip_through_a_file(const fs::path& dir) { const fs::path file = dir / "notizen.txt"; pda::TextBuffer buffer; buffer.append("Einkaufen"); buffer.append("Zahnarzt anrufen"); CHECK(buffer.modified()); CHECK(buffer.save(file).has_value()); CHECK(!buffer.modified()); pda::TextBuffer reloaded; CHECK(reloaded.load(file).has_value()); CHECK(reloaded.line_count() == 2U); CHECK(*reloaded.line(2U) == "Zahnarzt anrufen"); return 0; } int the_explorer_sees_what_we_wrote(const fs::path& dir) { const pda::Explorer explorer{dir}; const auto entries = explorer.list(); CHECK(entries.has_value()); CHECK(entries->size() == 2U); /* Alphabetisch: kontakte.tsv vor notizen.txt. */ CHECK((*entries)[0].name == "kontakte.tsv"); CHECK((*entries)[1].name == "notizen.txt"); CHECK((*entries)[0].size > 0U); return 0; } int the_calculator_agrees_with_arithmetic() { const auto value = pda::evaluate("(2 + 3) * 4 - 10 / 2"); CHECK(value.has_value()); CHECK(*value == 15.0); const auto broken = pda::evaluate("2 +"); CHECK(!broken.has_value()); CHECK(!pda::format_error(broken.error()).empty()); 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 { /* Eigenes Arbeitsverzeichnis, damit der Test nichts findet, was er nicht * selbst angelegt hat -- und nichts hinterlaesst. */ const fs::path dir = fs::current_path() / "integration.basics.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 += contacts_survive_a_save_and_load_cycle(dir); failures += notes_round_trip_through_a_file(dir); failures += the_explorer_sees_what_we_wrote(dir); failures += the_calculator_agrees_with_arithmetic(); fs::remove_all(dir, ec); if (failures != 0) return 1; std::puts("integration/basics: ok"); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "FAIL: unerwartete Exception: %s\n", error.what()); return 1; } }