diff --git a/test/debayer-geometry.cpp b/test/debayer-geometry.cpp
index cfc757fa..f2f0bc09 100644
--- a/test/debayer-geometry.cpp
+++ b/test/debayer-geometry.cpp
@@ -1,63 +1,106 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 /*
  * Copyright (C) 2026, Red Hat Inc.
  *
  * Sensor crop tests
  */
 
 #include <iostream>
 
 #include <libcamera/geometry.h>
 
 #include "libcamera/internal/software_isp/debayer_geometry.h"
 
 #include "test.h"
 
 using namespace libcamera;
 
 class DebayerGeometryTest : public Test
 {
 protected:
 	int run() override
 	{
-		if (testCrop())
+		if (testCrop() || testSensorCrop())
 			return TestFail;
 
 		return TestPass;
 	}
 
 private:
 	int testCrop()
 	{
 		static const struct {
 			Size input;
 			Size native;
 			Size output;
 			Rectangle expected;
 		} cases[] = {
 			{ { 1920, 1080 }, { 1920, 1080 }, { 1920, 1080 }, { 0, 0, 1920, 1080 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 1920, 1092 }, { 4, 0, 1920, 1092 } },
 			/* Output resolution changes must not shrink the sensor window. */
 			{ { 1928, 1092 }, { 1920, 1092 }, { 1920, 1080 }, { 4, 6, 1920, 1080 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 1280, 720 }, { 4, 6, 1920, 1080 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 640, 480 }, { 236, 0, 1456, 1092 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 512, 768 }, { 600, 0, 728, 1092 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 1280, 640 }, { 4, 66, 1920, 960 } },
 			/* A smaller Bayer border leaves an odd top coordinate. */
 			{ { 1928, 1092 }, { 1924, 1092 }, { 1280, 720 }, { 2, 5, 1924, 1082 } },
 		};
 
 		for (const auto &entry : cases) {
 			Rectangle crop = debayerCrop(entry.input, entry.native, entry.output);
 			if (crop != entry.expected) {
 				std::cerr << "Crop " << entry.input << " -> " << entry.output
 					  << " is " << crop << ", expected " << entry.expected << std::endl;
 				return TestFail;
 			}
 		}
 
 		return TestPass;
 	}
+
+	int testSensorCrop()
+	{
+		static const struct {
+			Rectangle crop;
+			Size input;
+			Rectangle analog;
+			Transform transform;
+			Rectangle expected;
+		} cases[] = {
+			/* GPU scaling keeps the full visible sensor region. */
+			{ { 4, 6, 1920, 1080 }, { 1928, 1092 }, { 0, 0, 1928, 1092 }, Transform::Identity, { 4, 6, 1920, 1080 } },
+			/* A full readout keeps its physical pixel-array origin. */
+			{ { 0, 0, 2000, 1500 }, { 2000, 1500 }, { 8, 8, 4000, 3000 }, Transform::Identity, { 8, 8, 4000, 3000 } },
+			/* Sensor crop offsets and 2x binning use native pixels. */
+			{ { 4, 6, 1920, 1080 }, { 1928, 1092 }, { 100, 200, 3856, 2184 }, Transform::Identity, { 108, 212, 3840, 2160 } },
+			/* CPU debayering crops to output size without scaling. */
+			{ { 324, 186, 1280, 720 }, { 1928, 1092 }, { 100, 200, 3856, 2184 }, Transform::Identity, { 748, 572, 2560, 1440 } },
+			/* Preserve an odd image boundary before statistics alignment. */
+			{ { 2, 5, 1924, 1082 }, { 1928, 1092 }, { 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 }, Transform::HFlip, { 166, 28, 40, 20 } },
+			{ { 2, 4, 20, 10 }, { 100, 60 }, { 10, 20, 200, 120 }, Transform::VFlip, { 14, 112, 40, 20 } },
+			{ { 2, 4, 20, 10 }, { 100, 60 }, { 10, 20, 200, 120 }, Transform::HVFlip, { 166, 112, 40, 20 } },
+			/* Transposition is undone before the flip. */
+			{ { 4, 2, 10, 20 }, { 60, 100 }, { 10, 20, 200, 120 }, Transform::Rot270, { 166, 28, 40, 20 } },
+			/* Fractional native coordinates and sizes truncate independently. */
+			{ { 1, 1, 3, 3 }, { 8, 8 }, { 10, 20, 12, 12 }, Transform::Identity, { 11, 21, 4, 4 } },
+		};
+
+		for (const auto &entry : cases) {
+			Rectangle crop = debayerCropToSensor(entry.crop, entry.input,
+							     entry.analog,
+							     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)
