diff --git a/test/debayer-geometry.cpp b/test/debayer-geometry.cpp
index 86d4248f..1d96fce1 100644
--- a/test/debayer-geometry.cpp
+++ b/test/debayer-geometry.cpp
@@ -23,7 +23,7 @@ class DebayerGeometryTest : public Test
 protected:
 	int run() override
 	{
-		if (testProjection() || testCrop())
+		if (testProjection() || testCrop() || testSensorCrop())
 			return TestFail;
 
 		return TestPass;
@@ -123,6 +123,48 @@ private:
 
 		return TestPass;
 	}
+
+	int testSensorCrop()
+	{
+		static const struct {
+			Rectangle crop;
+			Size input;
+			Rectangle analog;
+			Rectangle active;
+			Transform transform;
+			Rectangle expected;
+		} cases[] = {
+			/* GPU scaling keeps the full visible sensor region. */
+			{ { 4, 6, 1920, 1080 }, { 1928, 1092 }, { 0, 0, 1928, 1092 }, { 0, 0, 1928, 1092 }, Transform::Identity, { 4, 6, 1920, 1080 } },
+			/* Sensor crop offsets and 2x binning use native pixels. */
+			{ { 4, 6, 1920, 1080 }, { 1928, 1092 }, { 100, 200, 3856, 2184 }, { 8, 8, 4000, 3000 }, Transform::Identity, { 100, 204, 3840, 2160 } },
+			/* CPU debayering crops to output size without scaling. */
+			{ { 324, 186, 1280, 720 }, { 1928, 1092 }, { 100, 200, 3856, 2184 }, { 8, 8, 4000, 3000 }, Transform::Identity, { 740, 564, 2560, 1440 } },
+			/* Preserve an odd image boundary before statistics alignment. */
+			{ { 2, 5, 1924, 1082 }, { 1928, 1092 }, { 0, 0, 3856, 2184 }, { 0, 0, 3856, 2184 }, Transform::Identity, { 4, 10, 3848, 2164 } },
+			/* Undo sensor flips before reporting the native rectangle. */
+			{ { 2, 4, 20, 10 }, { 100, 60 }, { 10, 20, 200, 120 }, { 2, 4, 300, 200 }, Transform::HFlip, { 164, 24, 40, 20 } },
+			{ { 2, 4, 20, 10 }, { 100, 60 }, { 10, 20, 200, 120 }, { 2, 4, 300, 200 }, Transform::VFlip, { 12, 108, 40, 20 } },
+			{ { 2, 4, 20, 10 }, { 100, 60 }, { 10, 20, 200, 120 }, { 2, 4, 300, 200 }, Transform::HVFlip, { 164, 108, 40, 20 } },
+			/* Transposition is undone before the flip. */
+			{ { 4, 2, 10, 20 }, { 60, 100 }, { 10, 20, 200, 120 }, { 2, 4, 300, 200 }, Transform::Rot270, { 164, 24, 40, 20 } },
+			/* Fractional native coordinates and sizes truncate independently. */
+			{ { 1, 1, 3, 3 }, { 8, 8 }, { 10, 20, 12, 12 }, { 2, 4, 30, 40 }, Transform::Identity, { 9, 17, 4, 4 } },
+		};
+
+		for (const auto &entry : cases) {
+			Rectangle crop = debayerCropToSensor(entry.crop, entry.input,
+							     entry.analog, entry.active,
+							     entry.transform);
+			if (crop != entry.expected) {
+				std::cerr << "Sensor mapping of " << entry.crop << " in " << entry.input
+					  << " is " << crop << ", expected " << entry.expected << std::endl;
+				return TestFail;
+			}
+		}
+
+		return TestPass;
+	}
 };
 
 TEST_REGISTER(DebayerGeometryTest)
