mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] macos: pure property-tested clamp_decision for the mouse-clamp addon (5M-case test)
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
// Pure geometry for the off-window mouse-release clamp: no AppKit, no state, no I/O,
|
||||
// so the SAME code that ships in production (mouseclamp.mm includes this) can be
|
||||
// hammered by a standalone property test (clamp_decision_test.c). Keeping the
|
||||
// decision here, not inline in the .mm, is the whole point: the misfire risk lives
|
||||
// in this arithmetic, so this arithmetic is what the machine must prove.
|
||||
//
|
||||
// Contract (mirrors NSPointInRect: min-edge inclusive, max-edge exclusive):
|
||||
// release INSIDE the window -> {clamp=false}, location returned UNCHANGED.
|
||||
// release OUTSIDE the window -> {clamp=true}, location snapped strictly inside
|
||||
// the content rect (so Chromium's hit-test can
|
||||
// never return null, which is the crash).
|
||||
#ifndef MOUSECLAMP_DECISION_H
|
||||
#define MOUSECLAMP_DECISION_H
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
bool clamp; // replace the event with one at (x,y)? false => leave it alone
|
||||
double x, y; // snapped location; meaningful only when clamp == true
|
||||
} ClampDecision;
|
||||
|
||||
// p = release location in window coords
|
||||
// w = window size (winW, winH)
|
||||
// c = content rect in window coords (cx, cy, cw, ch)
|
||||
static inline ClampDecision clamp_decision(double px, double py,
|
||||
double winW, double winH,
|
||||
double cx, double cy,
|
||||
double cw, double ch) {
|
||||
ClampDecision d;
|
||||
// inside the window (titlebar included) -> untouched, byte-for-byte the same event
|
||||
if (px >= 0.0 && px < winW && py >= 0.0 && py < winH) {
|
||||
d.clamp = false;
|
||||
d.x = px;
|
||||
d.y = py;
|
||||
return d;
|
||||
}
|
||||
d.clamp = true;
|
||||
d.x = fmin(fmax(px, cx + 1.0), cx + cw - 1.0);
|
||||
d.y = fmin(fmax(py, cy + 1.0), cy + ch - 1.0);
|
||||
return d;
|
||||
}
|
||||
|
||||
#endif // MOUSECLAMP_DECISION_H
|
||||
@@ -0,0 +1,73 @@
|
||||
// Property test for clamp_decision() (the only misfire-prone part of the fix).
|
||||
// Compiles with plain clang, no node/electron/AppKit, so it runs anywhere fast.
|
||||
// Hammers millions of random window/content/point combinations and asserts the
|
||||
// three invariants the fix's safety rests on. Run via ./run-tests.sh.
|
||||
#include "clamp_decision.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static unsigned long g_seed = 88172645463325252ULL;
|
||||
static double frnd(double lo, double hi) { // xorshift -> [lo,hi)
|
||||
g_seed ^= g_seed << 13; g_seed ^= g_seed >> 7; g_seed ^= g_seed << 17;
|
||||
return lo + (hi - lo) * ((double)(g_seed >> 11) / (double)(1ULL << 53));
|
||||
}
|
||||
|
||||
static long fails = 0;
|
||||
static void check(int cond, const char *msg,
|
||||
double px, double py, double w, double h,
|
||||
double cx, double cy, double cw, double ch, ClampDecision d) {
|
||||
if (cond) return;
|
||||
if (fails < 12)
|
||||
fprintf(stderr,
|
||||
"FAIL %s: p=(%.2f,%.2f) win=(%.2f,%.2f) content=(%.2f,%.2f,%.2f,%.2f) "
|
||||
"=> clamp=%d (%.2f,%.2f)\n",
|
||||
msg, px, py, w, h, cx, cy, cw, ch, d.clamp, d.x, d.y);
|
||||
fails++;
|
||||
}
|
||||
|
||||
static void run(double px, double py, double w, double h,
|
||||
double cx, double cy, double cw, double ch) {
|
||||
ClampDecision d = clamp_decision(px, py, w, h, cx, cy, cw, ch);
|
||||
int inside = (px >= 0.0 && px < w && py >= 0.0 && py < h);
|
||||
if (inside) {
|
||||
// INVARIANT 1 (no misfire on normal clicks): in-window release is untouched.
|
||||
check(!d.clamp, "inside-must-not-clamp", px, py, w, h, cx, cy, cw, ch, d);
|
||||
check(d.x == px && d.y == py, "inside-must-be-identical", px, py, w, h, cx, cy, cw, ch, d);
|
||||
} else {
|
||||
// INVARIANT 2 (crash-safe): off-window release is clamped strictly inside the
|
||||
// content rect, so Chromium's hit-test can never return null.
|
||||
check(d.clamp, "outside-must-clamp", px, py, w, h, cx, cy, cw, ch, d);
|
||||
check(d.x >= cx + 1.0 && d.x <= cx + cw - 1.0, "x-in-content", px, py, w, h, cx, cy, cw, ch, d);
|
||||
check(d.y >= cy + 1.0 && d.y <= cy + ch - 1.0, "y-in-content", px, py, w, h, cx, cy, cw, ch, d);
|
||||
}
|
||||
// INVARIANT 3: never produces NaN/inf.
|
||||
check(d.x == d.x && d.y == d.y, "no-nan", px, py, w, h, cx, cy, cw, ch, d);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
// Hand-picked edges: exact corners, the off-by-one boundaries, second-display
|
||||
// negatives, the literal 650->499 case I verified on real events.
|
||||
run(0, 0, 500, 350, 0, 0, 500, 350); // top-left corner (inside)
|
||||
run(499, 349, 500, 350, 0, 0, 500, 350); // last inside pixel
|
||||
run(500, 175, 500, 350, 0, 0, 500, 350); // exactly on right edge (outside)
|
||||
run(650, 175, 500, 350, 0, 0, 500, 350); // the verified real case -> expect x=499
|
||||
run(-300, -900, 500, 350, 0, 0, 500, 350); // second display up-left (negatives)
|
||||
run(99999, 99999, 500, 350, 0, 0, 500, 350); // far off-window
|
||||
|
||||
const long N = 5000000;
|
||||
for (long i = 0; i < N; i++) {
|
||||
double w = frnd(80, 4000), h = frnd(80, 4000);
|
||||
double cw = frnd(50, w), ch = frnd(50, h);
|
||||
double cx = frnd(0, w - cw), cy = frnd(0, h - ch);
|
||||
double px = frnd(-3000, w + 3000), py = frnd(-3000, h + 3000);
|
||||
run(px, py, w, h, cx, cy, cw, ch);
|
||||
}
|
||||
|
||||
if (fails) {
|
||||
fprintf(stderr, "\nclamp_decision: %ld FAILURES across %ld cases\n", fails, N + 6);
|
||||
return 1;
|
||||
}
|
||||
printf("clamp_decision: ALL %ld cases pass (invariants: in-window untouched, "
|
||||
"off-window snapped strictly inside content, no NaN)\n", N + 6);
|
||||
return 0;
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <node_api.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <math.h>
|
||||
#include "clamp_decision.h"
|
||||
|
||||
static id gMonitor = nil;
|
||||
|
||||
@@ -28,15 +28,16 @@ static NSEvent *ClampOffWindowRelease(NSEvent *event) {
|
||||
if (!content) return event;
|
||||
NSPoint p = [event locationInWindow];
|
||||
NSSize ws = [win frame].size;
|
||||
// act only when the release is truly off the window (the crash case); any
|
||||
// release inside the window, titlebar included, is left exactly as-is
|
||||
if (NSPointInRect(p, NSMakeRect(0.0, 0.0, ws.width, ws.height))) return event;
|
||||
NSRect cb = [content frame];
|
||||
CGFloat x = fmin(fmax(p.x, NSMinX(cb) + 1.0), NSMaxX(cb) - 1.0);
|
||||
CGFloat y = fmin(fmax(p.y, NSMinY(cb) + 1.0), NSMaxY(cb) - 1.0);
|
||||
// all the misfire-prone arithmetic lives in clamp_decision() so the property
|
||||
// test exercises the exact code that ships
|
||||
ClampDecision d = clamp_decision(p.x, p.y, ws.width, ws.height,
|
||||
cb.origin.x, cb.origin.y,
|
||||
cb.size.width, cb.size.height);
|
||||
if (!d.clamp) return event;
|
||||
NSEvent *clamped =
|
||||
[NSEvent mouseEventWithType:t
|
||||
location:NSMakePoint(x, y)
|
||||
location:NSMakePoint(d.x, d.y)
|
||||
modifierFlags:[event modifierFlags]
|
||||
timestamp:[event timestamp]
|
||||
windowNumber:[event windowNumber]
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
# Compile + run the clamp_decision property test (plain clang, no electron deps).
|
||||
set -euo pipefail
|
||||
if ! command -v clang >/dev/null 2>&1; then
|
||||
echo "clang not found; skipping clamp_decision property test"; exit 0
|
||||
fi
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
BIN="$(mktemp -t clamp_decision_test)"
|
||||
trap 'rm -f "$BIN"' EXIT
|
||||
clang -O2 -Wall -Wextra -Werror -o "$BIN" "$HERE/clamp_decision_test.c"
|
||||
"$BIN"
|
||||
@@ -14,7 +14,8 @@
|
||||
"dist:win": "electron-builder --win --x64 --publish never",
|
||||
"dist:win:publish": "electron-builder --win --x64 --publish always",
|
||||
"dist:all": "electron-builder --mac --win --linux",
|
||||
"test": "node --test affiliateTracking.test.js"
|
||||
"test": "node --test affiliateTracking.test.js",
|
||||
"test:mouseclamp": "bash native/mouseclamp/run-tests.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-updater": "6.8.3",
|
||||
|
||||
Reference in New Issue
Block a user