129 lines
4.1 KiB
C++
129 lines
4.1 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2025 ai-ad4 and the FreePDFEditor contributors
|
|
//
|
|
// test/test_ipc.cpp — end-to-end test for the M1 IPC layer (§2.1, ADR-0004).
|
|
// Launches the sandboxed document process, opens a real PDF via the RPC
|
|
// client, and verifies the page count + ping round-trip. This is the M1
|
|
// foundation test: it proves the process split, the Cap'n Proto RPC over a
|
|
// socketpair, the seccomp sandbox, the SCM_RIGHTS fd handoff, and the QPDF
|
|
// read-from-handed-fd path all work together.
|
|
//
|
|
// Plain freestanding main (no GTest yet — matches the M0 test convention in
|
|
// test/CMakeLists.txt). Returns non-zero on failure.
|
|
|
|
#include "DocumentProcessClient.h"
|
|
#include "ProcessLauncher.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <unistd.h>
|
|
|
|
namespace {
|
|
|
|
// A minimal valid one-page PDF with a correct xref (QPDF --check clean).
|
|
// Written to a temp file by the test so the UI process can open() it and hand
|
|
// the fd to the sandboxed document process.
|
|
const char* kMinimalPdf =
|
|
"%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n"
|
|
"2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj\n"
|
|
"3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 612 792]>>endobj\n"
|
|
"xref\n0 4\n0000000000 65535 f \n0000000009 00000 n \n"
|
|
"0000000052 00000 n \n0000000101 00000 n \n"
|
|
"trailer<</Size 4/Root 1 0 R>>\nstartxref\n164\n%%EOF\n";
|
|
|
|
std::string write_temp_pdf()
|
|
{
|
|
char tmpl[] = "/tmp/fpe_ipc_test_XXXXXX";
|
|
int fd = ::mkstemp(tmpl);
|
|
if (fd < 0) { std::perror("mkstemp"); std::exit(2); }
|
|
// Append the .pdf extension to the mkstemp-generated name.
|
|
std::string path = std::string(tmpl) + ".pdf";
|
|
::close(fd);
|
|
::unlink(tmpl); // mkstemp created the placeholder; we want the .pdf name
|
|
{
|
|
std::ofstream f;
|
|
f.open(path, std::ios::binary | std::ios::trunc);
|
|
f << kMinimalPdf;
|
|
}
|
|
return path;
|
|
}
|
|
|
|
int fail(const char* msg)
|
|
{
|
|
std::fprintf(stderr, "FAIL: %s\n", msg);
|
|
return 1;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main()
|
|
{
|
|
std::string path = write_temp_pdf();
|
|
|
|
auto proc = freepdfeditor::ipc::launch_document_process(path.c_str());
|
|
if (!proc.client) {
|
|
::unlink(path.c_str());
|
|
return fail("launch_document_process returned null client");
|
|
}
|
|
|
|
// ping first — cheapest liveness check.
|
|
try {
|
|
if (!proc.client->ping("hello-ipc")) {
|
|
::unlink(path.c_str());
|
|
return fail("ping echo mismatch");
|
|
}
|
|
} catch (const std::exception& e) {
|
|
::unlink(path.c_str());
|
|
std::fprintf(stderr, "FAIL: ping threw: %s\n", e.what());
|
|
return 1;
|
|
}
|
|
|
|
// open — should report 1 page (the minimal PDF has one page).
|
|
freepdfeditor::ipc::OpenReply rep;
|
|
try {
|
|
rep = proc.client->open(path);
|
|
} catch (const std::exception& e) {
|
|
::unlink(path.c_str());
|
|
std::fprintf(stderr, "FAIL: open threw: %s\n", e.what());
|
|
return 1;
|
|
}
|
|
if (!rep.ok) {
|
|
::unlink(path.c_str());
|
|
std::fprintf(stderr, "FAIL: open returned ok=false: %s\n", rep.error.c_str());
|
|
return 1;
|
|
}
|
|
if (rep.pageCount != 1) {
|
|
::unlink(path.c_str());
|
|
std::fprintf(stderr, "FAIL: expected 1 page, got %u\n", rep.pageCount);
|
|
return 1;
|
|
}
|
|
if (rep.pdfVersion.empty()) {
|
|
::unlink(path.c_str());
|
|
return fail("empty pdfVersion");
|
|
}
|
|
|
|
// getPageCount — should agree with open.
|
|
try {
|
|
std::uint32_t n = proc.client->getPageCount();
|
|
if (n != 1) {
|
|
::unlink(path.c_str());
|
|
std::fprintf(stderr, "FAIL: getPageCount expected 1, got %u\n", n);
|
|
return 1;
|
|
}
|
|
} catch (const std::exception& e) {
|
|
::unlink(path.c_str());
|
|
std::fprintf(stderr, "FAIL: getPageCount threw: %s\n", e.what());
|
|
return 1;
|
|
}
|
|
|
|
// Destroying the client disconnects and reaps the child.
|
|
proc.client.reset();
|
|
|
|
std::printf("PASS: ipc end-to-end — ping ok, open ok (pageCount=%u, "
|
|
"pdfVersion=%s), getPageCount ok\n", rep.pageCount, rep.pdfVersion.c_str());
|
|
::unlink(path.c_str());
|
|
return 0;
|
|
} |