#include "catch.hpp" #include "utils.hpp" #include #include #include #include #include // The MockDecompressor behaves like other Decompressor classes, but "invents" // OSM data in XML format that can be read. Through a parameter to the // constructor it can be instructed to throw an exception in specific parts // of its code. This is then used to test the internals of the Reader. class MockDecompressor final : public osmium::io::Decompressor { std::string m_fail_in; int m_read_count = 0; public: explicit MockDecompressor(std::string fail_in) : m_fail_in(std::move(fail_in)) { if (m_fail_in == "constructor") { throw std::runtime_error{"error constructor"}; } } MockDecompressor(const MockDecompressor&) = delete; MockDecompressor& operator=(const MockDecompressor&) = delete; MockDecompressor(MockDecompressor&&) = delete; MockDecompressor& operator=(MockDecompressor&&) = delete; ~MockDecompressor() noexcept override = default; static void add_node(std::string& s, int i) { s += "\n"; } std::string read() override { std::string buffer; ++m_read_count; if (m_read_count == 1) { if (m_fail_in == "first read") { throw std::runtime_error{"error first read"}; } buffer += "\n\n"; for (int i = 0; i < 1000; ++i) { add_node(buffer, i); } } else if (m_read_count == 2) { if (m_fail_in == "second read") { throw std::runtime_error{"error second read"}; } for (int i = 1000; i < 2000; ++i) { add_node(buffer, i); } } else if (m_read_count == 3) { buffer += ""; } return buffer; } void close() override { if (m_fail_in == "close") { throw std::runtime_error{"error close"}; } } }; // class MockDecompressor std::string fail_in; TEST_CASE("Test Reader using MockDecompressor") { osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::gzip, [](int /*unused*/, osmium::io::fsync /*unused*/) { return nullptr; }, [&](int /*unused*/) { return new MockDecompressor(fail_in); }, [](const char* /*unused*/, size_t /*unused*/) { return nullptr; } ); SECTION("fail in constructor") { fail_in = "constructor"; try { const osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")}; REQUIRE(false); } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error constructor"); } } SECTION("fail in first read") { fail_in = "first read"; try { osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")}; reader.read(); REQUIRE(false); } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error first read"); } } SECTION("fail in second read") { fail_in = "second read"; try { osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")}; reader.read(); reader.read(); REQUIRE(false); } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error second read"); } } SECTION("fail in close") { fail_in = "close"; try { osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")}; reader.read(); reader.read(); reader.read(); reader.close(); REQUIRE(false); } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error close"); } } SECTION("not failing") { fail_in = "not"; osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")}; reader.read(); reader.close(); REQUIRE(true); } }