srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_capture_session.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_capture_session.cpp')
-rw-r--r--tests/test_capture_session.cpp138
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);
+}