Files
video-shader-toys/tests/OscServerTests.cpp
Aiden 0560ea3c49
Some checks failed
CI / Native Windows Build And Tests (push) Failing after 7s
CI / React UI Build (push) Has been cancelled
CI / Windows Release Package (push) Has been cancelled
osc multi arguments
2026-05-03 14:51:57 +10:00

214 lines
6.6 KiB
C++

#include "OscServer.h"
#include <cstring>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
namespace
{
int gFailures = 0;
void Expect(bool condition, const char* message)
{
if (condition)
return;
std::cerr << "FAIL: " << message << "\n";
++gFailures;
}
void AppendPaddedString(std::vector<char>& packet, const std::string& text)
{
packet.insert(packet.end(), text.begin(), text.end());
packet.push_back('\0');
while (packet.size() % 4 != 0)
packet.push_back('\0');
}
void AppendInt32(std::vector<char>& packet, int value)
{
const unsigned int bits = static_cast<unsigned int>(value);
packet.push_back(static_cast<char>((bits >> 24) & 0xff));
packet.push_back(static_cast<char>((bits >> 16) & 0xff));
packet.push_back(static_cast<char>((bits >> 8) & 0xff));
packet.push_back(static_cast<char>(bits & 0xff));
}
void AppendFloat32(std::vector<char>& packet, float value)
{
unsigned int bits = 0;
std::memcpy(&bits, &value, sizeof(bits));
AppendInt32(packet, static_cast<int>(bits));
}
void AppendFloat64(std::vector<char>& packet, double value)
{
uint64_t bits = 0;
std::memcpy(&bits, &value, sizeof(bits));
for (int shift = 56; shift >= 0; shift -= 8)
packet.push_back(static_cast<char>((bits >> shift) & 0xff));
}
std::vector<char> BuildOscPacket(const std::string& address, const std::string& typeTags)
{
std::vector<char> packet;
AppendPaddedString(packet, address);
AppendPaddedString(packet, typeTags);
return packet;
}
}
struct OscServerTestAccess
{
using Message = OscServer::OscMessage;
static bool Decode(OscServer& server, const std::vector<char>& packet, Message& message, std::string& error)
{
return server.DecodeMessage(packet.data(), static_cast<int>(packet.size()), message, error);
}
static bool Dispatch(OscServer& server, const Message& message, std::string& error)
{
return server.DispatchMessage(message, error);
}
static void SetUpdateParameterCallback(
OscServer& server,
const std::function<bool(const std::string&, const std::string&, const std::string&, std::string&)>& callback)
{
server.mCallbacks.updateParameter = callback;
}
};
namespace
{
void TestDecodeFloatMessage()
{
OscServer server;
std::vector<char> packet = BuildOscPacket("/VideoShaderToys/VHS/intensity", ",f");
AppendFloat32(packet, 0.75f);
OscServerTestAccess::Message message;
std::string error;
Expect(OscServerTestAccess::Decode(server, packet, message, error), "float OSC message decodes");
Expect(message.address == "/VideoShaderToys/VHS/intensity", "float OSC address is preserved");
Expect(message.valueJson.find("0.75") == 0, "float OSC value becomes JSON number");
}
void TestDecodeDoubleMessage()
{
OscServer server;
std::vector<char> packet = BuildOscPacket("/VideoShaderToys/fisheye-reproject/panDegrees", ",d");
AppendFloat64(packet, 51.5);
OscServerTestAccess::Message message;
std::string error;
Expect(OscServerTestAccess::Decode(server, packet, message, error), "double OSC message decodes");
Expect(message.address == "/VideoShaderToys/fisheye-reproject/panDegrees", "double OSC address is preserved");
Expect(message.valueJson.find("51.5") == 0, "double OSC value becomes JSON number");
}
void TestDecodeVectorMessage()
{
OscServer server;
std::vector<char> packet = BuildOscPacket("/VideoShaderToys/video-transform/pan", ",ff");
AppendFloat32(packet, 0.25f);
AppendFloat32(packet, -0.5f);
OscServerTestAccess::Message message;
std::string error;
Expect(OscServerTestAccess::Decode(server, packet, message, error), "multi-float OSC message decodes");
Expect(message.address == "/VideoShaderToys/video-transform/pan", "multi-float OSC address is preserved");
Expect(message.valueJson.find("[0.25,-0.5") == 0, "multi-float OSC value becomes JSON array");
}
void TestDecodeIntStringAndBoolMessages()
{
OscServer server;
std::vector<char> intPacket = BuildOscPacket("/VideoShaderToys/layer-1/mode", ",i");
AppendInt32(intPacket, 3);
OscServerTestAccess::Message intMessage;
std::string error;
Expect(OscServerTestAccess::Decode(server, intPacket, intMessage, error), "int OSC message decodes");
Expect(intMessage.valueJson == "3", "int OSC value becomes JSON number");
std::vector<char> stringPacket = BuildOscPacket("/VideoShaderToys/layer-1/mode", ",s");
AppendPaddedString(stringPacket, "equisolid");
OscServerTestAccess::Message stringMessage;
error.clear();
Expect(OscServerTestAccess::Decode(server, stringPacket, stringMessage, error), "string OSC message decodes");
Expect(stringMessage.valueJson == "\"equisolid\"", "string OSC value becomes JSON string");
std::vector<char> boolPacket = BuildOscPacket("/VideoShaderToys/layer-1/enabled", ",T");
OscServerTestAccess::Message boolMessage;
error.clear();
Expect(OscServerTestAccess::Decode(server, boolPacket, boolMessage, error), "boolean OSC message decodes");
Expect(boolMessage.valueJson == "true", "true OSC typetag becomes JSON boolean");
}
void TestDispatchValidAddress()
{
OscServer server;
std::string layerKey;
std::string parameterKey;
std::string valueJson;
OscServerTestAccess::SetUpdateParameterCallback(server, [&](const std::string& layer, const std::string& parameter, const std::string& value, std::string&)
{
layerKey = layer;
parameterKey = parameter;
valueJson = value;
return true;
});
OscServerTestAccess::Message message;
message.address = "/VideoShaderToys/VHS/intensity";
message.valueJson = "0.5";
std::string error;
Expect(OscServerTestAccess::Dispatch(server, message, error), "valid OSC control address dispatches");
Expect(layerKey == "VHS", "dispatch extracts layer key");
Expect(parameterKey == "intensity", "dispatch extracts parameter key");
Expect(valueJson == "0.5", "dispatch forwards JSON value");
}
void TestRejectsUnsupportedAddress()
{
OscServer server;
bool called = false;
OscServerTestAccess::SetUpdateParameterCallback(server, [&](const std::string&, const std::string&, const std::string&, std::string&)
{
called = true;
return true;
});
OscServerTestAccess::Message message;
message.address = "/OtherApp/VHS/intensity";
message.valueJson = "0.5";
std::string error;
Expect(!OscServerTestAccess::Dispatch(server, message, error), "unsupported OSC namespace is rejected");
Expect(!called, "unsupported OSC namespace does not invoke callback");
Expect(!error.empty(), "unsupported OSC address reports an error");
}
}
int main()
{
TestDecodeFloatMessage();
TestDecodeDoubleMessage();
TestDecodeVectorMessage();
TestDecodeIntStringAndBoolMessages();
TestDispatchValidAddress();
TestRejectsUnsupportedAddress();
if (gFailures != 0)
{
std::cerr << gFailures << " OscServer test failure(s).\n";
return 1;
}
std::cout << "OscServer tests passed.\n";
return 0;
}