Hand a card dragged onto a plugin's own editor to the OS as a file drop; withdraw the OLE drag when the pointer returns to REAPER
A code-ownership proof (window class procedure's module, via the pure window_origin) plus REAPER's FX-surface token routes a plugin editor to OLE; FX chrome keeps the instrument drop. The OLE loop re-asks the law each turn, so passing a plugin editor stays reversible.
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
// Standalone tests for reasampler::ui::window_origin — no REAPER, no Win32, no test framework.
|
||||
// Asserts the per-window code fold and the chain rule the plugin-editor hand-off proof rests on.
|
||||
|
||||
#include "../src/core/ui/window_origin.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace reasampler::ui;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond) do { if(!(cond)) { \
|
||||
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
||||
|
||||
static const std::string kWinDir = "C:\\Windows";
|
||||
|
||||
static WindowFacts inModule(const std::string& path, bool host = false) {
|
||||
WindowFacts f;
|
||||
f.procResolved = true;
|
||||
f.hostModule = host;
|
||||
f.modulePath = path;
|
||||
return f;
|
||||
}
|
||||
|
||||
// --- classifyWindowCode --------------------------------------------------------
|
||||
|
||||
// REAPER's own chrome and containers: the class procedure lives in the executable.
|
||||
static void testHostExecutableIsHost() {
|
||||
CHECK(classifyWindowCode(inModule("C:\\Program Files\\REAPER (x64)\\reaper.exe", true),
|
||||
kWinDir) == WindowCode::Host);
|
||||
}
|
||||
|
||||
// Standard controls and the dialog class are implemented by OS modules, wherever the OS keeps
|
||||
// them — System32, SysWOW64 for a 32-bit host, and the side-by-side store comctl32 v6 loads from.
|
||||
static void testOsModulesAreSystem() {
|
||||
CHECK(classifyWindowCode(inModule("C:\\Windows\\System32\\user32.dll"), kWinDir) ==
|
||||
WindowCode::System);
|
||||
CHECK(classifyWindowCode(inModule("C:\\Windows\\SysWOW64\\user32.dll"), kWinDir) ==
|
||||
WindowCode::System);
|
||||
CHECK(classifyWindowCode(
|
||||
inModule("C:\\Windows\\WinSxS\\amd64_microsoft.windows.common-controls_6595b64144ccf1df"
|
||||
"_6.0.26100.1_none_x\\comctl32.dll"),
|
||||
kWinDir) == WindowCode::System);
|
||||
}
|
||||
|
||||
// The same directory spelled differently by two APIs still compares equal: case, separator
|
||||
// direction, a trailing separator on the root, and the long-path prefix.
|
||||
static void testSystemMatchIgnoresSpelling() {
|
||||
CHECK(classifyWindowCode(inModule("c:/WINDOWS/system32/USER32.DLL"), kWinDir) ==
|
||||
WindowCode::System);
|
||||
CHECK(classifyWindowCode(inModule("C:\\Windows\\System32\\user32.dll"), "C:\\WINDOWS\\") ==
|
||||
WindowCode::System);
|
||||
CHECK(classifyWindowCode(inModule("\\\\?\\C:\\Windows\\System32\\user32.dll"), kWinDir) ==
|
||||
WindowCode::System);
|
||||
}
|
||||
|
||||
// Containment, not string prefix: a sibling directory whose name merely starts with the OS
|
||||
// directory's is plugin territory, and so is the OS directory itself named as a file path.
|
||||
static void testSiblingDirectoryIsNotSystem() {
|
||||
CHECK(classifyWindowCode(inModule("C:\\WindowsApps\\thing.dll"), kWinDir) ==
|
||||
WindowCode::Plugin);
|
||||
CHECK(classifyWindowCode(inModule("C:\\Windows2\\thing.dll"), kWinDir) == WindowCode::Plugin);
|
||||
CHECK(classifyWindowCode(inModule("C:\\Windows"), kWinDir) == WindowCode::Plugin);
|
||||
}
|
||||
|
||||
// A plugin binary anywhere outside the OS directory — the VST3 bundle, a VST2 folder, a helper
|
||||
// DLL a plugin loads beside itself — is Plugin.
|
||||
static void testPluginBinariesArePlugin() {
|
||||
CHECK(classifyWindowCode(
|
||||
inModule("C:\\Program Files\\Common Files\\VST3\\Serum2.vst3\\Contents\\x86_64-win\\"
|
||||
"Serum2.vst3"),
|
||||
kWinDir) == WindowCode::Plugin);
|
||||
CHECK(classifyWindowCode(inModule("D:\\VstPlugins\\Qt5Widgets.dll"), kWinDir) ==
|
||||
WindowCode::Plugin);
|
||||
}
|
||||
|
||||
// Another process's window is plugin code by the process boundary alone, whatever its other facts
|
||||
// claim: a procedure address from another process resolved in ours is meaningless.
|
||||
static void testOtherProcessIsPluginRegardlessOfModule() {
|
||||
WindowFacts f = inModule("C:\\Program Files\\REAPER (x64)\\reaper.exe", true);
|
||||
f.otherProcess = true;
|
||||
CHECK(classifyWindowCode(f, kWinDir) == WindowCode::Plugin);
|
||||
WindowFacts g;
|
||||
g.otherProcess = true; // nothing resolved at all
|
||||
CHECK(classifyWindowCode(g, kWinDir) == WindowCode::Plugin);
|
||||
}
|
||||
|
||||
// No module (a charset-mismatch handle, generated code) is no evidence — never Plugin.
|
||||
static void testUnresolvedProcIsUnknown() {
|
||||
WindowFacts f;
|
||||
f.modulePath = "C:\\VST\\x.dll"; // ignored without a resolved procedure
|
||||
CHECK(classifyWindowCode(f, kWinDir) == WindowCode::Unknown);
|
||||
}
|
||||
|
||||
// Without the OS directory a standard control cannot be told from plugin code, so every non-host
|
||||
// module degrades to Unknown — the failure must not read REAPER's buttons as a plugin.
|
||||
static void testMissingSystemRootNeverYieldsPlugin() {
|
||||
CHECK(classifyWindowCode(inModule("C:\\Windows\\System32\\user32.dll"), "") ==
|
||||
WindowCode::Unknown);
|
||||
CHECK(classifyWindowCode(inModule("C:\\VST\\x.dll"), "") == WindowCode::Unknown);
|
||||
CHECK(classifyWindowCode(inModule("C:\\VST\\x.dll"), "\\") == WindowCode::Unknown);
|
||||
CHECK(classifyWindowCode(inModule("C:\\REAPER\\reaper.exe", true), "") == WindowCode::Host);
|
||||
}
|
||||
|
||||
// --- isPluginEditorChain -------------------------------------------------------
|
||||
|
||||
// Pointer on REAPER's FX-window chrome: a standard control or a REAPER container, under the
|
||||
// dialog-class root. No plugin code anywhere in the chain.
|
||||
static void testChromeChainIsNotPlugin() {
|
||||
CHECK(!isPluginEditorChain({WindowCode::System, WindowCode::Host, WindowCode::System}));
|
||||
CHECK(!isPluginEditorChain({WindowCode::Host, WindowCode::System}));
|
||||
CHECK(!isPluginEditorChain({WindowCode::Host}));
|
||||
}
|
||||
|
||||
// Pointer on the plugin's editor window itself.
|
||||
static void testPluginWindowChainIsPlugin() {
|
||||
CHECK(isPluginEditorChain({WindowCode::Plugin, WindowCode::Host, WindowCode::System}));
|
||||
}
|
||||
|
||||
// A standard control INSIDE the plugin's editor (a native text box) is System on its own; the
|
||||
// plugin's window above it is what makes the point the plugin's.
|
||||
static void testStandardControlInsidePluginIsPlugin() {
|
||||
CHECK(isPluginEditorChain(
|
||||
{WindowCode::System, WindowCode::Plugin, WindowCode::Host, WindowCode::System}));
|
||||
}
|
||||
|
||||
// A bridged plugin's editor embedded across the process boundary: the hit window is Plugin by
|
||||
// process, REAPER's container and root above it are in-process.
|
||||
static void testBridgedEmbeddedEditorIsPlugin() {
|
||||
WindowFacts bridged;
|
||||
bridged.otherProcess = true;
|
||||
CHECK(isPluginEditorChain({classifyWindowCode(bridged, kWinDir), WindowCode::Host,
|
||||
WindowCode::System}));
|
||||
}
|
||||
|
||||
// Unknown anywhere, and nothing else, is not proof; an empty chain (no window at all) is not.
|
||||
static void testUnknownAndEmptyChainsAreNotPlugin() {
|
||||
CHECK(!isPluginEditorChain({WindowCode::Unknown, WindowCode::Host, WindowCode::System}));
|
||||
CHECK(!isPluginEditorChain({WindowCode::Unknown}));
|
||||
CHECK(!isPluginEditorChain({}));
|
||||
}
|
||||
|
||||
int main() {
|
||||
testHostExecutableIsHost();
|
||||
testOsModulesAreSystem();
|
||||
testSystemMatchIgnoresSpelling();
|
||||
testSiblingDirectoryIsNotSystem();
|
||||
testPluginBinariesArePlugin();
|
||||
testOtherProcessIsPluginRegardlessOfModule();
|
||||
testUnresolvedProcIsUnknown();
|
||||
testMissingSystemRootNeverYieldsPlugin();
|
||||
|
||||
testChromeChainIsNotPlugin();
|
||||
testPluginWindowChainIsPlugin();
|
||||
testStandardControlInsidePluginIsPlugin();
|
||||
testBridgedEmbeddedEditorIsPlugin();
|
||||
testUnknownAndEmptyChainsAreNotPlugin();
|
||||
|
||||
if (g_fail == 0) std::printf("All tests passed.\n");
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user