diff options
Diffstat (limited to 'tests/test_capture_session.cpp')
| -rw-r--r-- | tests/test_capture_session.cpp | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/tests/test_capture_session.cpp b/tests/test_capture_session.cpp new file mode 100644 index 0000000..691131a --- /dev/null +++ b/tests/test_capture_session.cpp @@ -0,0 +1,138 @@ +#include <doctest/doctest.h> +#include <pcap.h> +#include <unistd.h> + +#include <cstdio> +#include <string> +#include <vector> + +#include "wireframe/capture_session.hpp" +#include "wireframe/pcapng/writer.hpp" + +using namespace wireframe; + +namespace { + +// A real, named pcapng file on disk - CaptureSession::open() takes a +// path, not a FILE*, so a std::tmpfile() (unnamed) doesn't work here +// the way it does in test_pcapng.cpp. Cleans itself up via RAII. +struct TempPcapngFile { + std::string path; + + explicit TempPcapngFile(int link_type, const std::vector<std::vector<unsigned char>>& packets) { + char path_template[] = "/tmp/wireframe_test_XXXXXX"; + int fd = mkstemp(path_template); + REQUIRE(fd != -1); + path = path_template; + + std::FILE* f = fdopen(fd, "wb"); + REQUIRE(f != nullptr); + pcapng::Writer writer(f); + writer.write_section_header(); + writer.write_interface_description(65535, static_cast<std::uint16_t>(link_type)); + std::uint32_t ts = 1700000000; + for (const auto& packet : packets) { + writer.write_packet(0, ts++, 0, packet, + static_cast<std::uint32_t>(packet.size())); + } + std::fclose(f); + } + + ~TempPcapngFile() { std::remove(path.c_str()); } +}; + +} // namespace + +TEST_CASE("is_supported_datalink accepts EN10MB and RAW, rejects others") { + CHECK(is_supported_datalink(DLT_EN10MB)); + CHECK(is_supported_datalink(DLT_RAW)); + CHECK_FALSE(is_supported_datalink(DLT_IEEE802_11)); +} + +TEST_CASE("CaptureSession::open reports an error for a nonexistent device, without needing root") { + CaptureSession session; + CaptureSessionOptions options; + options.device = "this-device-does-not-exist-0xdeadbeef"; + + auto err = session.open(options); + REQUIRE(err.has_value()); + CHECK_FALSE(err->empty()); +} + +TEST_CASE("CaptureSession::stats returns nullopt before open()") { + CaptureSession session; + CHECK_FALSE(session.stats().has_value()); +} + +TEST_CASE("CaptureSession::open replays a pcapng file without needing root or a live device") { + TempPcapngFile file(DLT_EN10MB, {{0xDE, 0xAD}, {0xBE, 0xEF}}); + + CaptureSession session; + CaptureSessionOptions options; + options.replay_input_path = file.path; + + CHECK_FALSE(session.open(options).has_value()); + CHECK(session.is_replay()); + CHECK(session.datalink() == DLT_EN10MB); + CHECK(session.device() == file.path); + CHECK_FALSE(session.stats().has_value()); // pcap_stats() needs a live handle; replay has none +} + +TEST_CASE("CaptureSession::open rejects combining -r (replay) with -f (capture filter)") { + TempPcapngFile file(DLT_EN10MB, {{0x01}}); + + CaptureSession session; + CaptureSessionOptions options; + options.replay_input_path = file.path; + options.filter_expr = "tcp"; + + auto err = session.open(options); + REQUIRE(err.has_value()); + CHECK(err->find("-r") != std::string::npos); +} + +TEST_CASE("CaptureSession::open reports an error for a nonexistent replay file") { + CaptureSession session; + CaptureSessionOptions options; + options.replay_input_path = "/tmp/this-file-does-not-exist-0xdeadbeef.pcapng"; + + auto err = session.open(options); + REQUIRE(err.has_value()); + CHECK_FALSE(err->empty()); +} + +TEST_CASE("CaptureSession::open reports an error for a pcapng file with no packets") { + TempPcapngFile file(DLT_EN10MB, {}); // just SHB + IDB, no EPBs + + CaptureSession session; + CaptureSessionOptions options; + options.replay_input_path = file.path; + + auto err = session.open(options); + REQUIRE(err.has_value()); +} + +TEST_CASE("replayed packets reach the CaptureQueue in order, and the thread stops on its own") { + TempPcapngFile file(DLT_RAW, {{0x01, 0x02}, {0x03, 0x04}, {0x05, 0x06}}); + + CaptureSession session; + CaptureSessionOptions options; + options.replay_input_path = file.path; + REQUIRE_FALSE(session.open(options).has_value()); + CHECK(session.datalink() == DLT_RAW); + + CaptureQueue queue(4096); + auto capture_thread = session.start_capture_thread(queue); + + std::vector<unsigned char> first_bytes; + int count = 0; + while (auto packet = queue.pop()) { + if (count == 0) first_bytes = packet->data; + ++count; + } + capture_thread.join(); + + CHECK(count == 3); + REQUIRE(first_bytes.size() == 2); + CHECK(first_bytes[0] == 0x01); +} |