Files
AnyPS5/core/libs/tests/GuestWebrtcFileWrapper.cpp
T

132 lines
5.3 KiB
C++

#include "prx/libwebrtc/libwebrtc.hpp"
#include "prx/libc/include/Api.hpp"
#include "prx/libc/include/FileStream.hpp"
#include "tests/TemporaryFile.hpp"
#include <array>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
namespace {
constexpr auto Close = _ZN6webrtc11FileWrapper5CloseEv_nid_postfix;
constexpr auto Flush = _ZN6webrtc11FileWrapper5FlushEv_nid_postfix;
constexpr auto Write = _ZN6webrtc11FileWrapper5WriteEPKvm_nid_postfix;
constexpr auto Move = _ZN6webrtc11FileWrapperC1EOS0__nid_postfix;
void Require(bool condition, const char* message) {
if (!condition) {
std::fprintf(stderr, "WebRTC file wrapper: %s\n", message);
std::abort();
}
}
template <typename TCall>
void RequireThrows(TCall call) {
try { call(); }
catch (const std::invalid_argument&) { return; }
Require(false, "invalid object or operation must throw");
}
void RoundTrip() {
FileStream stream(TemporaryFile::Open());
WebRtcFileWrapper wrapper{&stream};
const std::array<unsigned char, 8> bytes{0, 255, 13, 10, 26, 128, 1, 42};
Require(Write(&wrapper, nullptr, 0), "zero-length write succeeds");
Require(Write(&wrapper, bytes.data(), 3), "write first chunk");
Require(Write(&wrapper, bytes.data() + 3, bytes.size() - 3), "write second chunk");
Require(Flush(&wrapper), "flush data");
Require(wrapper.file == &stream, "flush retains ownership");
Require(ftell_nid_postfix(&stream) == 8, "write exactly the requested bytes");
Require(fseek_nid_postfix(&stream, 0, SEEK_SET) == 0, "rewind");
std::array<unsigned char, 10> result{};
result.fill(0x5a);
Require(fread_nid_postfix(result.data() + 1, 1, bytes.size(), &stream) == bytes.size(), "read back bytes");
Require(std::memcmp(result.data() + 1, bytes.data(), bytes.size()) == 0, "binary data is preserved");
Require(result.front() == 0x5a && result.back() == 0x5a, "read preserves guards");
RequireThrows([&] { Write(&wrapper, nullptr, 1); });
Require(Close(&wrapper), "close succeeds");
Require(wrapper.file == nullptr && !stream.IsOpen(), "close clears wrapper and closes stream");
Require(Close(&wrapper), "closing twice succeeds");
}
void Ownership() {
FileStream stream(TemporaryFile::Open());
WebRtcFileWrapper source{&stream};
WebRtcFileWrapper destination;
std::memset(&destination, 0xa5, sizeof(destination));
Move(&destination, &source);
Require(source.file == nullptr && destination.file == &stream, "move transfers ownership");
Require(Close(&source) && stream.IsOpen(), "moved-from close does not close destination");
Require(Write(&destination, "moved", 5), "moved-to wrapper can write");
Require(Close(&destination) && !stream.IsOpen(), "moved-to wrapper closes stream");
WebRtcFileWrapper empty{};
Move(&destination, &empty);
Require(destination.file == nullptr && empty.file == nullptr, "move of empty wrapper");
Require(Close(&destination), "close moved empty wrapper");
}
void WriteFailure() {
FileStream stream(TemporaryFile::Open());
#ifdef _WIN32
const int descriptor = _dup(_fileno(stream.GetHandle()));
Require(descriptor >= 0, "duplicate stream descriptor");
auto* native = _fdopen(descriptor, "rb");
#else
const int descriptor = dup(fileno(stream.GetHandle()));
Require(descriptor >= 0, "duplicate stream descriptor");
auto* native = fdopen(descriptor, "rb");
#endif
Require(native != nullptr, "create read-only stream");
FileStream reader(native);
WebRtcFileWrapper wrapper{&reader};
Require(!Write(&wrapper, "x", 1), "read-only write returns false");
Require(wrapper.file == &reader, "failed write retains ownership");
Require(Close(&wrapper), "close read-only stream");
Require(stream.Close() == 0, "close original stream");
}
#ifdef __linux__
void BufferedFailures() {
for (bool flushFirst : {false, true}) {
auto* native = std::fopen("/dev/full", "wb");
Require(native != nullptr, "open full device");
Require(std::setvbuf(native, nullptr, _IOFBF, 4096) == 0, "enable buffering");
FileStream stream(native);
WebRtcFileWrapper wrapper{&stream};
Require(Write(&wrapper, "data", 4), "buffer write before device failure");
if (flushFirst) {
Require(!Flush(&wrapper), "flush reports device failure");
Require(wrapper.file == &stream && stream.IsOpen(), "failed flush retains ownership");
Close(&wrapper);
} else {
Require(!Close(&wrapper), "close reports implicit flush failure");
}
Require(wrapper.file == nullptr && !stream.IsOpen(), "failed close still releases ownership");
Require(Close(&wrapper), "close after error succeeds");
}
}
#endif
}
int main() {
RoundTrip();
Ownership();
WriteFailure();
#ifdef __linux__
BufferedFailures();
#endif
WebRtcFileWrapper empty{};
Require(Close(&empty), "close unopened wrapper");
RequireThrows([] { Close(nullptr); });
RequireThrows([] { Flush(nullptr); });
RequireThrows([] { Write(nullptr, "x", 1); });
RequireThrows([&] { Flush(&empty); });
RequireThrows([&] { Write(&empty, "x", 1); });
RequireThrows([&] { Move(nullptr, &empty); });
RequireThrows([&] { Move(&empty, nullptr); });
RequireThrows([&] { Move(&empty, &empty); });
}