diff options
Diffstat (limited to 'testing/web-platform/tests/shape-detection')
66 files changed, 2206 insertions, 0 deletions
diff --git a/testing/web-platform/tests/shape-detection/META.yml b/testing/web-platform/tests/shape-detection/META.yml new file mode 100644 index 0000000000..7754624047 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/META.yml @@ -0,0 +1,3 @@ +spec: https://wicg.github.io/shape-detection-api/ +suggested_reviewers: + - yellowdoge diff --git a/testing/web-platform/tests/shape-detection/README.md b/testing/web-platform/tests/shape-detection/README.md new file mode 100644 index 0000000000..624b021336 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/README.md @@ -0,0 +1,63 @@ +The `shapedetection-helpers.js` tests require implementations of +the `FaceDetectionTest`, `BarcodeDetectionTest` and `TextDetectionTest` +interfaces, which should emulate platform shape detection backends. + +The `FaceDetectionTest` interface is defined as: + +``` + class FaceDetectionTest { + async initialize(); // Sets up the testing environment. + async reset(); // Frees the resources. + MockFaceDetectionProvider(); //Returns `MockFaceDetectionProvider` interface. + }; + + class MockFaceDetectionProvider { + getFrameData(); //Gets frame data of detection result. + getMaxDetectedFaces(); //Gets value of `maxDetectedFaces` in `FaceDetector` constructor + getFastMode(); //Gets value of `fastMode` in `FaceDetector` constructor + }; +``` + +The Chromium implementation of the `FaceDetectionTest` interface is located in +[mock-facedetection.js](../resources/chromium/mock-facedetection.js). + +The `BarcodeDetectionTest` interface is defined as: + +``` + class BarcodeDetectionTest { + async initialize(); // Sets up the testing environment. + async reset(); // Frees the resources. + MockBarcodeDetectionProvider(); //Returns `MockBarcodeDetectionProvider` interface. + }; + + class MockBarcodeDetectionProvider { + async enumerateSupportedFormats(); //Enumerates supported formats + getFrameData(); //Gets frame data of detection result. + getFormats(); //Gets value of `formats` in `BarcodeDetector` constructor + simulateNoImplementation(); // simulate a 'no implementation available' case + }; +``` + +The Chromium implementation of the `BarcodeDetectionTest` interface is located in +[mock-barcodedetection.js](../resources/chromium/mock-barcodedetection.js). + +The `TextDetectionTest` interface is defined as: + +``` + class TextDetectionTest { + async initialize(); // Sets up the testing environment. + async reset(); // Frees the resources. + MockTextDetection(); //Returns `MockTextDetection` interface. + }; + + class MockTextDetection { + getFrameData(); //Gets frame data of detection result. + }; +``` + +The Chromium implementation of the `TextDetectionTest` interface is located in +[mock-textdetection.js](../resources/chromium/mock-textdetection.js). + +Other browser vendors should provide their own implementations of +the `FaceDetectionTest`, `BarcodeDetectionTest` and `TextDetectionTest` +interfaces. diff --git a/testing/web-platform/tests/shape-detection/detected-boundingBox-read-only.https.html b/testing/web-platform/tests/shape-detection/detected-boundingBox-read-only.https.html new file mode 100644 index 0000000000..dcf379b97a --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detected-boundingBox-read-only.https.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +// These tests verify that detected{Face, Barcode, Text}'s boundingBox +// should be DOMRectReadOnly. +const imageDataTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + mockTestName: "FaceDetectionTest", + name: "Face - detectedFace.boundingBox should be DOMRectReadOnly" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + mockTestName: "BarcodeDetectionTest", + name: "Barcode - detectedBarcode.boundingBox should be DOMRectReadOnly" + }, + { + createDetector: () => { return new TextDetector(); }, + mockTestName: "TextDetectionTest", + name: "Text - detectedText.boundingBox should be DOMRectReadOnly" + } + ]; + +for (let imageDataTest of imageDataTests) { + detection_test(imageDataTest.mockTestName, async t => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = "/images/green-16x16.png"; + await imgWatcher.wait_for("load"); + + const canvas = document.createElement("canvas"); + canvas.getContext("2d").drawImage(img, 0, 0); + + const detector = imageDataTest.createDetector(); + const detectionResult = await detector.detect(canvas.getContext("2d") + .getImageData(0, 0, canvas.width, canvas.height)); + CheckDetectedReadOnlyBoundingBoxes(detectionResult); + }, imageDataTest.name); +} + +function CheckDetectedReadOnlyBoundingBoxes(detectedObjects) { + const properties = + ['x', 'y', 'width', 'height', 'top', 'right', 'bottom', 'left']; + + detectedObjects.forEach(detectedObject => { + properties.forEach(property => { + assert_readonly(detectedObject.boundingBox, property); + }); + }); +} + +</script> diff --git a/testing/web-platform/tests/shape-detection/detected-postMessage.https.html b/testing/web-platform/tests/shape-detection/detected-postMessage.https.html new file mode 100644 index 0000000000..8066984b26 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detected-postMessage.https.html @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +// These tests verify that Detected{Face, Barcode, Text} can be passed to +// postMessage(). +const postMessageTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + mockTestName: "FaceDetectionTest", + detectionResultTest: FaceDetectorDetectionResultTest, + name: "Face - DetectedFace can be passed to postMessage()" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + mockTestName: "BarcodeDetectionTest", + detectionResultTest: BarcodeDetectorDetectionResultTest, + name: "Barcode - DetectedBarcode can be passed to postMessage()" + }, + { + createDetector: () => { return new TextDetector(); }, + mockTestName: "TextDetectionTest", + detectionResultTest: TextDetectorDetectionResultTest, + name: "Text - DetectedText can be passed to postMessage()", + }, + ]; + +for (let postMessageTest of postMessageTests) { + detection_test(postMessageTest.mockTestName, async t => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = "/images/green-16x16.png"; + await imgWatcher.wait_for("load"); + + const canvas = document.createElement("canvas"); + canvas.getContext("2d").drawImage(img, 0, 0); + + const detector = postMessageTest.createDetector(); + const detectionResult = await detector.detect(canvas.getContext("2d") + .getImageData(0, 0, canvas.width, canvas.height)); + + const msgWatcher = new EventWatcher(t, window, ['message']); + window.postMessage(detectionResult); + const evt = await msgWatcher.wait_for('message'); + postMessageTest.detectionResultTest(evt.data) + }, postMessageTest.name); +} + +function FaceDetectorDetectionResultTest(detectionResult) { + assert_equals(detectionResult.length, 3, "Number of faces"); + assert_equals(detectionResult[0].landmarks.length, 2, "Number of landmarks"); + assert_object_equals(detectionResult[0].landmarks[0], + {type : 'eye', locations : [{x : 4.0, y : 5.0}]}, + "landmark #1"); + assert_equals(detectionResult[0].landmarks[1].locations.length, 8, + "Number of locations along eye"); + assert_object_equals(detectionResult[1].landmarks[0], + {type : 'nose', locations : [{x : 100.0, y : 50.0}]}, + "landmark #2"); + assert_equals(detectionResult[1].landmarks[1].locations.length, 9, + "Number of locations along nose"); +} + +function BarcodeDetectorDetectionResultTest(detectionResult) { + assert_equals(detectionResult.length, 2, "Number of barcodes"); + assert_equals(detectionResult[0].rawValue, "cats", "barcode 1"); + assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format"); + assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2"); + assert_equals(detectionResult[1].format, "code_128", "barcode 2 format"); +} + +function TextDetectorDetectionResultTest(detectionResult) { + assert_equals(detectionResult.length, 2, "Number of textBlocks"); + assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1"); + assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2"); + for (let i = 0; i < detectionResult.length; i++) { + assert_equals(detectionResult[i].boundingBox.x, detectionResult[i].cornerPoints[0].x); + assert_equals(detectionResult[i].boundingBox.y, detectionResult[i].cornerPoints[0].y); + assert_equals(detectionResult[i].boundingBox.width, + detectionResult[i].cornerPoints[2].x - detectionResult[i].cornerPoints[3].x); + assert_equals(detectionResult[i].boundingBox.height, + detectionResult[i].cornerPoints[2].y - detectionResult[i].cornerPoints[1].y); + } + +} + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-Blob.https.window.js b/testing/web-platform/tests/shape-detection/detection-Blob.https.window.js new file mode 100644 index 0000000000..401a9cc72d --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-Blob.https.window.js @@ -0,0 +1,19 @@ +'use strict'; + +promise_test(async (t) => { + const blob = new Blob(['not really a png'], {type: 'image/png'}); + const detector = new FaceDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(blob)); +}, 'FaceDetector.detect() rejects on a Blob'); + +promise_test(async (t) => { + const blob = new Blob(['not really a png'], {type: 'image/png'}); + const detector = new BarcodeDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(blob)); +}, 'BarcodeDetector.detect() rejects on a Blob'); + +promise_test(async (t) => { + const blob = new Blob(['not really a png'], {type: 'image/png'}); + const detector = new TextDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(blob)); +}, 'TextDetector.detect() rejects on a Blob'); diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLCanvasElement.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLCanvasElement.https.html new file mode 100644 index 0000000000..4773bc8a66 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-HTMLCanvasElement.https.html @@ -0,0 +1,125 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +// These tests verify that a Detector's detect() works on an HTMLCanvasElement +// and on an OffscreenCanvas. +const canvasElementTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + createCanvas: () => { return document.createElement("canvas"); }, + pixelFormat: "uint8", + mockTestName: "FaceDetectionTest", + detectionResultTest: FaceDetectorDetectionResultTest, + name: "Face - detect(HTMLCanvasElement)" + }, + { + createDetector: () => { return new FaceDetector(); }, + createCanvas: () => { return document.createElement("canvas"); }, + pixelFormat: "float16", + mockTestName: "FaceDetectionTest", + detectionResultTest: FaceDetectorDetectionResultTest, + name: "Face - detect(HTMLCanvasElementF16Format)" + }, + { + createDetector: () => { return new FaceDetector(); }, + createCanvas: () => { return new OffscreenCanvas(300, 150); }, + pixelFormat: "uint8", + mockTestName: "FaceDetectionTest", + detectionResultTest: FaceDetectorDetectionResultTest, + name: "Face - detect(OffscreenCanvas)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + createCanvas: () => { return document.createElement("canvas"); }, + pixelFormat: "uint8", + mockTestName: "BarcodeDetectionTest", + detectionResultTest: BarcodeDetectorDetectionResultTest, + name: "Barcode - detect(HTMLCanvasElement)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + createCanvas: () => { return document.createElement("canvas"); }, + pixelFormat: "float16", + mockTestName: "BarcodeDetectionTest", + detectionResultTest: BarcodeDetectorDetectionResultTest, + name: "Barcode - detect(HTMLCanvasElementF16Format)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + createCanvas: () => { return new OffscreenCanvas(300, 150); }, + pixelFormat: "uint8", + mockTestName: "BarcodeDetectionTest", + detectionResultTest: BarcodeDetectorDetectionResultTest, + name: "Barcode - detect(OffscreenCanvas)" + }, + { + createDetector: () => { return new TextDetector(); }, + createCanvas: () => { return document.createElement("canvas"); }, + pixelFormat: "uint8", + mockTestName: "TextDetectionTest", + detectionResultTest: TextDetectorDetectionResultTest, + name: "Text - detect(HTMLCanvasElement)" + }, + { + createDetector: () => { return new TextDetector(); }, + createCanvas: () => { return document.createElement("canvas"); }, + pixelFormat: "float16", + mockTestName: "TextDetectionTest", + detectionResultTest: TextDetectorDetectionResultTest, + name: "Text - detect(HTMLCanvasElementF16Format)" + }, + { + createDetector: () => { return new TextDetector(); }, + createCanvas: () => { return new OffscreenCanvas(300, 150); }, + pixelFormat: "uint8", + mockTestName: "TextDetectionTest", + detectionResultTest: TextDetectorDetectionResultTest, + name: "Text - detect(OffscreenCanvas)" + } + ]; + +for (let canvasElementTest of canvasElementTests) { + detection_test(canvasElementTest.mockTestName, async (t, detectionTest) => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = "/images/green-16x16.png"; + await imgWatcher.wait_for("load"); + const canvas = canvasElementTest.createCanvas(); + canvas.getContext( + "2d", { pixelFormat: canvasElementTest.pixelFormat }).drawImage( + img, 0, 0); + + const detector = canvasElementTest.createDetector(); + const detectionResult = await detector.detect(canvas); + canvasElementTest.detectionResultTest(detectionResult, detectionTest); + }, canvasElementTest.name); +} + +function FaceDetectorDetectionResultTest(detectionResult, mockTest) { + const imageReceivedByMock = + mockTest.MockFaceDetectionProvider().getFrameData(); + assert_equals(imageReceivedByMock.byteLength, 180000, "Image length"); + const GREEN_PIXEL = 0xFF00FF00; + assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color"); + assert_equals(detectionResult.length, 3, "Number of faces"); +} + +function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of barcodes"); + assert_equals(detectionResult[0].rawValue, "cats", "barcode 1"); + assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format"); + assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2"); + assert_equals(detectionResult[1].format, "code_128", "barcode 2 format"); +} + +function TextDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of textBlocks"); + assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1"); + assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2"); +} + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-empty-src.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-empty-src.https.html new file mode 100644 index 0000000000..40eee24494 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-empty-src.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +// This test verifies *Detector.detect() rejects the promise when fed with +// an HTMLImageElement with an empty 'src'. +const emptyInputTests = [ + { + createDetector: () => { return new FaceDetector(); }, + name: "Face - detect(empty src)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + name: "Barcode - detect(empty src)" + }, + { + createDetector: () => { return new TextDetector(); }, + name: "Text - detect(empty src)" + } +]; + +for (let emptyInputTest of emptyInputTests) { + promise_test(async t => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = ""; + await imgWatcher.wait_for("error"); + + const detector = emptyInputTest.createDetector(); + promise_rejects_dom(t, "InvalidStateError", detector.detect(img)); + }, emptyInputTest.name); +} +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-zero-dimension-image.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-zero-dimension-image.https.html new file mode 100644 index 0000000000..621dd4e23f --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement-zero-dimension-image.https.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +// This test verifies *Detector.detect() returns an empty list when fed with +// an HTMLImageElement with an image with 0x0 dimensions. +const zeroDimensionsTests = [ + { + createDetector: () => { return new FaceDetector(); }, + name: "Face - detect(0x0)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + name: "Barcode - detect(0x0)" + }, + { + createDetector: () => { return new TextDetector(); }, + name: "Text - detect(0x0)" + } +]; + +for (let zeroDimensionsTest of zeroDimensionsTests) { + promise_test(async t => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = "/images/red-zerosize.svg"; + await imgWatcher.wait_for("load"); + + const detector = zeroDimensionsTest.createDetector(); + const detectionResult = await detector.detect(img); + assert_equals(detectionResult.length, 0); + }, zeroDimensionsTest.name); +} +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLImageElement.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement.https.html new file mode 100644 index 0000000000..f3b994c258 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-HTMLImageElement.https.html @@ -0,0 +1,86 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<body> +<img id="img" src="/images/green-16x16.png"/> +</body> +<script> + +// These tests verify that a Detector's detect() works on an HTMLImageElement. +const imageElementTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + mockTestName: "FaceDetectionTest", + detectionResultTest: FaceDetectorDetectionResultTest, + name: "Face - detect(HTMLImageElement)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + mockTestName: "BarcodeDetectionTest", + detectionResultTest: BarcodeDetectorDetectionResultTest, + name: "Barcode - detect(HTMLImageElement)", + }, + { + createDetector: () => { return new TextDetector(); }, + mockTestName: "TextDetectionTest", + detectionResultTest: TextDetectorDetectionResultTest, + name: "Text - detect(HTMLImageElement)" + } + ]; + +for (let imageElementTest of imageElementTests) { + detection_test(imageElementTest.mockTestName, async (t, detectionTest) => { + const img = document.getElementById("img"); + + const detector = imageElementTest.createDetector(); + const detectionResult = await detector.detect(img); + imageElementTest.detectionResultTest(detectionResult, detectionTest); + }, imageElementTest.name); +} + +function FaceDetectorDetectionResultTest(detectionResult, mockTest) { + const imageReceivedByMock = + mockTest.MockFaceDetectionProvider().getFrameData(); + assert_equals(imageReceivedByMock.byteLength, 1024, "Image length"); + const GREEN_PIXEL = 0xFF00FF00; + assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color"); + assert_equals(detectionResult.length, 3, "Number of faces"); + assert_equals(detectionResult[0].landmarks.length, 2, "Number of landmarks"); + assert_object_equals(detectionResult[0].landmarks[0], + {type : 'eye', locations : [{x : 4.0, y : 5.0}]}, + "landmark #1"); + assert_equals(detectionResult[0].landmarks[1].locations.length, 8, + "Number of locations along eye"); + assert_object_equals(detectionResult[1].landmarks[0], + {type : 'nose', locations : [{x : 100.0, y : 50.0}]}, + "landmark #2"); + assert_equals(detectionResult[1].landmarks[1].locations.length, 9, + "Number of locations along nose"); +} + +function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of barcodes"); + assert_equals(detectionResult[0].rawValue, "cats", "barcode 1"); + assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format"); + assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2"); + assert_equals(detectionResult[1].format, "code_128", "barcode 2 format"); +} + +function TextDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of textBlocks"); + assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1"); + assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2"); + for (let i = 0; i < detectionResult.length; i++) { + assert_equals(detectionResult[i].boundingBox.x, detectionResult[i].cornerPoints[0].x); + assert_equals(detectionResult[i].boundingBox.y, detectionResult[i].cornerPoints[0].y); + assert_equals(detectionResult[i].boundingBox.width, + detectionResult[i].cornerPoints[2].x - detectionResult[i].cornerPoints[3].x); + assert_equals(detectionResult[i].boundingBox.height, + detectionResult[i].cornerPoints[2].y - detectionResult[i].cornerPoints[1].y); + } + +} + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-HTMLVideoElement.https.html b/testing/web-platform/tests/shape-detection/detection-HTMLVideoElement.https.html new file mode 100644 index 0000000000..2ce379bafc --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-HTMLVideoElement.https.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +// These tests verify that a Detector's detect() works on an HTMLVideoElement. +const videoElementTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + mockTestName: "FaceDetectionTest", + detectionResultTest: FaceDetectorDetectionResultTest, + name: "Face - detect(HTMLVideoElement)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + mockTestName: "BarcodeDetectionTest", + detectionResultTest: BarcodeDetectorDetectionResultTest, + name: "Barcode - detect(HTMLVideoElement)" + }, + { + createDetector: () => { return new TextDetector(); }, + mockTestName: "TextDetectionTest", + detectionResultTest: TextDetectorDetectionResultTest, + name: "Text - detect(HTMLVideoElement)" + } + ]; + +for (let videoElementTest of videoElementTests) { + detection_test(videoElementTest.mockTestName, async (t, detectionTest) => { + const video = document.createElement("video"); + video.src = "/media/white.webm"; + video.loop = true; + video.autoplay = true; + const videoWatcher = new EventWatcher(t, video, ["play", "error"]); + video.load(); + await videoWatcher.wait_for("play"); + + const detector = videoElementTest.createDetector(); + const detectionResult = await detector.detect(video); + videoElementTest.detectionResultTest(detectionResult, detectionTest); + }, videoElementTest.name); +} + +function FaceDetectorDetectionResultTest(detectionResult, mockTest) { + const imageReceivedByMock = + mockTest.MockFaceDetectionProvider().getFrameData(); + assert_equals(imageReceivedByMock.byteLength, 307200, "Image length"); + assert_equals(detectionResult.length, 3, "Number of faces"); +} + +function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of barcodes"); + assert_equals(detectionResult[0].rawValue, "cats", "barcode 1"); + assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format"); + assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2"); + assert_equals(detectionResult[1].format, "code_128", "barcode 2 format"); +} + +function TextDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of textBlocks"); + assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1"); + assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2"); +} + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-ImageBitmap-closed.https.window.js b/testing/web-platform/tests/shape-detection/detection-ImageBitmap-closed.https.window.js new file mode 100644 index 0000000000..80cb373f33 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-ImageBitmap-closed.https.window.js @@ -0,0 +1,44 @@ +'use strict'; + +async function createClosedImageBitmap(t) { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ['load', 'error']); + img.src = '/images/green-16x16.png'; + await imgWatcher.wait_for('load'); + const imageBitmap = await createImageBitmap(img); + imageBitmap.close(); + return imageBitmap; +} + +promise_test(async (t) => { + const imageBitmap = await createClosedImageBitmap(t); + const detector = new FaceDetector(); + try { + await detector.detect(imageBitmap); + assert_unreached(); + } catch (e) { + assert_equals(e.code, DOMException.INVALID_STATE_ERR); + } +}, 'FaceDetector.detect() rejects on a closed ImageBitmap'); + +promise_test(async (t) => { + const imageBitmap = await createClosedImageBitmap(t); + const detector = new BarcodeDetector(); + try { + await detector.detect(imageBitmap); + assert_unreached(); + } catch (e) { + assert_equals(e.code, DOMException.INVALID_STATE_ERR); + } +}, 'BarcodeDetector.detect() rejects on a closed ImageBitmap'); + +promise_test(async (t) => { + const imageBitmap = await createClosedImageBitmap(t); + const detector = new TextDetector(); + try { + await detector.detect(imageBitmap); + assert_unreached(); + } catch (e) { + assert_equals(e.code, DOMException.INVALID_STATE_ERR); + } +}, 'TextDetector.detect() rejects on a closed ImageBitmap'); diff --git a/testing/web-platform/tests/shape-detection/detection-ImageBitmap.https.html b/testing/web-platform/tests/shape-detection/detection-ImageBitmap.https.html new file mode 100644 index 0000000000..b4302c4a51 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-ImageBitmap.https.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +// These tests verify that a Detector's detect() works on an ImageBitmap. +const imageBitmapTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + mockTestName: "FaceDetectionTest", + detectionResultTest: FaceDetectorDetectionResultTest, + name: "Face - detect(ImageBitmap)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + mockTestName: "BarcodeDetectionTest", + detectionResultTest: BarcodeDetectorDetectionResultTest, + name: "Barcode - detect(ImageBitmap)" + }, + { + createDetector: () => { return new TextDetector(); }, + mockTestName: "TextDetectionTest", + detectionResultTest: TextDetectorDetectionResultTest, + name: "Text - detect(ImageBitmap)", + } + ]; + +for (let imageBitmapTest of imageBitmapTests) { + detection_test(imageBitmapTest.mockTestName, async (t, detectionTest) => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = "/images/green-16x16.png"; + await imgWatcher.wait_for("load"); + const imageBitmap = await createImageBitmap(img); + + const detector = imageBitmapTest.createDetector(); + const detectionResult = await detector.detect(imageBitmap); + imageBitmapTest.detectionResultTest(detectionResult, detectionTest); + }, imageBitmapTest.name); +} + +function FaceDetectorDetectionResultTest(detectionResult, mockTest) { + const imageReceivedByMock = mockTest.MockFaceDetectionProvider().getFrameData(); + assert_equals(imageReceivedByMock.byteLength, 1024, "Image length"); + const GREEN_PIXEL = 0xFF00FF00; + assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color"); + assert_equals(detectionResult.length, 3, "Number of faces"); +} + +function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of barcodes"); + assert_equals(detectionResult[0].rawValue, "cats", "barcode 1"); + assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format"); + assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2"); + assert_equals(detectionResult[1].format, "code_128", "barcode 2 format"); +} + +function TextDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of textBlocks"); + assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1"); + assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2"); +} + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-ImageData-detached.https.html b/testing/web-platform/tests/shape-detection/detection-ImageData-detached.https.html new file mode 100644 index 0000000000..b4e31c8656 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-ImageData-detached.https.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + +function detachBuffer(buffer) { + window.postMessage('', '*', [buffer]); +} + +promise_test(async () => { + let data = new ImageData(1024, 1024); + detachBuffer(data.data.buffer); + let detector = new FaceDetector(); + try { + await detector.detect(data); + assert_unreached(); + } catch (e) { + assert_equals(e.code, DOMException.INVALID_STATE_ERR); + } +}, 'FaceDetector.detect() rejects on a detached buffer'); + +promise_test(async () => { + let data = new ImageData(1024, 1024); + detachBuffer(data.data.buffer); + let detector = new BarcodeDetector(); + try { + await detector.detect(data); + assert_unreached(); + } catch (e) { + assert_equals(e.code, DOMException.INVALID_STATE_ERR); + } +}, 'BarcodeDetector.detect() rejects on a detached buffer'); + +promise_test(async () => { + let data = new ImageData(1024, 1024); + detachBuffer(data.data.buffer); + let detector = new TextDetector(); + try { + await detector.detect(data); + assert_unreached(); + } catch (e) { + assert_equals(e.code, DOMException.INVALID_STATE_ERR); + } +}, 'TextDetector.detect() rejects on a detached buffer'); + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-ImageData.https.html b/testing/web-platform/tests/shape-detection/detection-ImageData.https.html new file mode 100644 index 0000000000..330239fdf0 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-ImageData.https.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +// These tests verify that a Detector's detect() works on an ImageBitmap. +const imageDataTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + mockTestName: "FaceDetectionTest", + detectionResultTest: FaceDetectorDetectionResultTest, + name: "Face - detect(ImageData)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + mockTestName: "BarcodeDetectionTest", + detectionResultTest: BarcodeDetectorDetectionResultTest, + name: "Barcode - detect(ImageData)" + }, + { + createDetector: () => { return new TextDetector(); }, + mockTestName: "TextDetectionTest", + detectionResultTest: TextDetectorDetectionResultTest, + name: "Text - detect(ImageData)", + } + ]; + +for (let imageDataTest of imageDataTests) { + detection_test(imageDataTest.mockTestName, async (t, detectionTest) => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = "/images/green-16x16.png"; + await imgWatcher.wait_for("load"); + const canvas = document.createElement("canvas"); + canvas.getContext("2d").drawImage(img, 0, 0); + + const detector = imageDataTest.createDetector(); + const detectionResult = await detector.detect(canvas.getContext("2d") + .getImageData(0, 0, canvas.width, canvas.height)); + imageDataTest.detectionResultTest(detectionResult, detectionTest); + }, imageDataTest.name); +} + +function FaceDetectorDetectionResultTest(detectionResult, mockTest) { + const imageReceivedByMock = mockTest.MockFaceDetectionProvider().getFrameData(); + assert_equals(imageReceivedByMock.byteLength, 180000,"Image length"); + const GREEN_PIXEL = 0xFF00FF00; + assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color"); + assert_equals(detectionResult.length, 3, "Number of faces"); +} + +function BarcodeDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of barcodes"); + assert_equals(detectionResult[0].rawValue, "cats", "barcode 1"); + assert_equals(detectionResult[0].format, "qr_code", "barcode 1 format"); + assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2"); + assert_equals(detectionResult[1].format, "code_128", "barcode 2 format"); +} + +function TextDetectorDetectionResultTest(detectionResult, mockTest) { + assert_equals(detectionResult.length, 2, "Number of textBlocks"); + assert_equals(detectionResult[0].rawValue, "cats", "textBlock 1"); + assert_equals(detectionResult[1].rawValue, "dogs", "textBlock 2"); +} + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-ImageDataUint16StorageFormat.https.window.js b/testing/web-platform/tests/shape-detection/detection-ImageDataUint16StorageFormat.https.window.js new file mode 100644 index 0000000000..1afe087abf --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-ImageDataUint16StorageFormat.https.window.js @@ -0,0 +1,39 @@ +// META: script=/shape-detection/resources/shapedetection-helpers.js + +const imgUint16 = new ImageData(1024, 1024, {storageFormat: 'uint16'}); + +// These tests verify that a Detector's detect() can process ImageData with +// uint16 storage format. +const imageDataTests = [ + { + createDetector: () => { + return new FaceDetector(); + }, + mockTestName: 'FaceDetectionTest', + name: + 'FaceDetector.detect() can process uint16 storage format ImageData' + }, + { + createDetector: () => { + return new BarcodeDetector(); + }, + mockTestName: 'BarcodeDetectionTest', + name: + 'BarcodeDetector.detect() can process uint16 storage format ImageData' + }, + { + createDetector: () => { + return new TextDetector(); + }, + mockTestName: 'TextDetectionTest', + name: + 'TextDetector.detect() can process uint16 storage format ImageData' + } +]; + +for (let imageDataTest of imageDataTests) { + detection_test(imageDataTest.mockTestName, async () => { + let detector = imageDataTest.createDetector(); + await detector.detect(imgUint16); + }, imageDataTest.name); +} diff --git a/testing/web-platform/tests/shape-detection/detection-SVGImageElement.https.window.js b/testing/web-platform/tests/shape-detection/detection-SVGImageElement.https.window.js new file mode 100644 index 0000000000..22f1629ecc --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-SVGImageElement.https.window.js @@ -0,0 +1,19 @@ +'use strict'; + +promise_test(async (t) => { + const image = document.createElementNS("http://www.w3.org/2000/svg", "image"); + const detector = new FaceDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(image)); +}, 'FaceDetector.detect() rejects on an SVGImageElement'); + +promise_test(async (t) => { + const image = document.createElementNS("http://www.w3.org/2000/svg", "image"); + const detector = new BarcodeDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(image)); +}, 'BarcodeDetector.detect() rejects on an SVGImageElement'); + +promise_test(async (t) => { + const image = document.createElementNS("http://www.w3.org/2000/svg", "image"); + const detector = new TextDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(image)); +}, 'TextDetector.detect() rejects on an SVGImageElement'); diff --git a/testing/web-platform/tests/shape-detection/detection-VideoFrame.https.window.js b/testing/web-platform/tests/shape-detection/detection-VideoFrame.https.window.js new file mode 100644 index 0000000000..601c60bcaf --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-VideoFrame.https.window.js @@ -0,0 +1,24 @@ +'use strict'; + +function createVideoFrame() { + const canvas = document.createElement('canvas'); + return new VideoFrame(canvas, {timestamp: 0}); +} + +promise_test(async (t) => { + const frame = createVideoFrame(); + const detector = new FaceDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(frame)); +}, 'FaceDetector.detect() rejects on a VideoFrame'); + +promise_test(async (t) => { + const frame = createVideoFrame(); + const detector = new BarcodeDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(frame)); +}, 'BarcodeDetector.detect() rejects on a VideoFrame'); + +promise_test(async (t) => { + const frame = createVideoFrame(); + const detector = new TextDetector(); + await promise_rejects_dom(t, 'NotSupportedError', detector.detect(frame)); +}, 'TextDetector.detect() rejects on a VideoFrame'); diff --git a/testing/web-platform/tests/shape-detection/detection-getSupportedFormats.https.html b/testing/web-platform/tests/shape-detection/detection-getSupportedFormats.https.html new file mode 100644 index 0000000000..0b4b223369 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-getSupportedFormats.https.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<link rel="help" href="https://wicg.github.io/shape-detection-api/#dom-barcodedetector-getsupportedformats"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +detection_test('BarcodeDetectionTest', async t => { + const result = await BarcodeDetector.getSupportedFormats(); + assert_equals(result.length, 3, 'Number of supported formats'); + assert_equals(result[0], 'aztec', 'format 1'); + assert_equals(result[1], 'data_matrix', 'format 2'); + assert_equals(result[2], 'qr_code', 'format 3'); +}, 'get supported barcode formats'); + +detection_test('BarcodeDetectionTest', async (t, detectionTest) => { + // Disable built-in support for barcode detection to test fallback handling. + detectionTest.MockBarcodeDetectionProvider().simulateNoImplementation(); + + const result = await BarcodeDetector.getSupportedFormats(); + assert_equals(result.length, 0, 'result.length'); + +}, 'getSupportedFormats() resolves with empty list when unsupported'); + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-on-worker.https.worker.js b/testing/web-platform/tests/shape-detection/detection-on-worker.https.worker.js new file mode 100644 index 0000000000..3981c6fdc8 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-on-worker.https.worker.js @@ -0,0 +1,54 @@ +importScripts("/resources/testharness.js"); +importScripts("/resources/test-only-api.js"); +importScripts("resources/shapedetection-helpers.js"); + +'use strict'; + +// These tests verify that a Detector's detect() works on an +// ImageBitmap on workers. +const imageBitmapTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + mockTestName: "FaceDetectionTest", + resultSize: 3, // Number of faces + detectorType: "Face" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + mockTestName: "BarcodeDetectionTest", + resultSize: 2, // Number of barcodes + detectorType: "Barcode" + }, + { + createDetector: () => { return new TextDetector(); }, + mockTestName: "TextDetectionTest", + resultSize: 2, // Number of text blocks + detectorType: "Text" + } + ]; + +for (let imageBitmapTest of imageBitmapTests) { + // ImageBitmap is of transferable type and can be sent to and + // tested on worker. + detection_test(imageBitmapTest.mockTestName, async (t, detectionTest) => { + const img = createTestImage(); + const theImageBitmap = await createImageBitmap(img); + const detector = imageBitmapTest.createDetector(); + const detectionResult = await detector.detect(theImageBitmap); + assert_equals(detectionResult.length, imageBitmapTest.resultSize, + `Number of ${imageBitmapTest.detectorType}`); + }, `${imageBitmapTest.detectorType} Detector detect(ImageBitmap) on worker`); +} + +function createTestImage() { + const image = new OffscreenCanvas(100, 50); + const imgctx = image.getContext('2d'); + imgctx.fillStyle = "#F00"; + imgctx.fillRect(0, 0, 2, 2); + imgctx.fillStyle = "#0F0"; + imgctx.fillRect(0, 0, 1, 1); + return image; +} + +done(); diff --git a/testing/web-platform/tests/shape-detection/detection-options.https.html b/testing/web-platform/tests/shape-detection/detection-options.https.html new file mode 100644 index 0000000000..4b79da2a6e --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-options.https.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<body> +<img id="img" src="/images/green-16x16.png"/> +</body> +<script> + +detection_test("FaceDetectionTest", async (t, detectionTest) => { + const img = document.getElementById("img"); + const mock = detectionTest.MockFaceDetectionProvider(); + + const detectorWithDefault = new FaceDetector(); + let faceDetectionResult = await detectorWithDefault.detect(img); + assert_equals(mock.getMaxDetectedFaces(), 10, "default maxDetectedFaces"); + assert_equals(mock.getFastMode(), false, "default maxDetectedFaces"); + + const detectorWithOptions = + new FaceDetector({maxDetectedFaces: 7, fastMode: true}); + faceDetectionResult = await detectorWithOptions.detect(img); + assert_equals(mock.getMaxDetectedFaces(), 7, "maxDetectedFaces"); + assert_equals(mock.getFastMode(), true, "maxDetectedFaces"); +}, "Test that FaceDetectionOptions are correctly propagated"); + +detection_test("BarcodeDetectionTest", async (t, detectionTest) => { + const img = document.getElementById("img"); + const mock = detectionTest.MockBarcodeDetectionProvider(); + + const detectorWithNoOptions = new BarcodeDetector(); + let barcodeDetectionResult = await detectorWithNoOptions.detect(img); + assert_array_equals(mock.getFormats(), [], "formats"); + + const detectorWithOptions = new BarcodeDetector({ + formats: ["code_128", "qr_code"]}); + barcodeDetectionResult = await detectorWithOptions.detect(img); + assert_array_equals( + mock.getFormats(), + [BarcodeFormat.CODE_128, BarcodeFormat.QR_CODE], + "formats"); + + const invalidFormats = [ + [], + ["unknown"], + ["foo", "bar"] + ]; + + invalidFormats.forEach(invalidFormat => { + assert_throws_js(TypeError, () => new BarcodeDetector({formats: invalidFormat})); + }); + +}, "Test that BarcodeDetectorOptions are correctly propagated"); + +</script> diff --git a/testing/web-platform/tests/shape-detection/detection-security-test.https.html b/testing/web-platform/tests/shape-detection/detection-security-test.https.html new file mode 100644 index 0000000000..4d87238dad --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detection-security-test.https.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +// Detectors should reject undecodable images with an InvalidStateError. +const badImageTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + name: "Face - detect(broken image)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + name: "Barcode - detect(broken image)" + }, + { + createDetector: () => { return new TextDetector(); }, + name: "Text - detect(broken image)" + } + ]; + +for (let badImageTest of badImageTests) { + // This test verifies that a Detector will reject an undecodable image. + promise_test(async t => { + const img = new Image(); + const error = + await detectOnElementAndExpectError(badImageTest.createDetector, + img, "/images/broken.png"); + assert_equals(error.name, "InvalidStateError"); + }, badImageTest.name); +} + +// Detectors should reject undecodable videos with an InvalidStateError. +const badVideoTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + name: "Face - detect(broken video)" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + name: "Barcode - detect(broken video)" + }, + { + createDetector: () => { return new TextDetector(); }, + name: "Text - detect(broken video)" + } + ]; + +for (let badVideoTest of badVideoTests) { + // This test verifies that a Detector will reject a broken video. + promise_test(async t => { + const video = document.createElement('video'); + const error = + await detectOnElementAndExpectError(badVideoTest.createDetector, + video, "garbage.webm"); + assert_equals(error.name, "InvalidStateError"); + }, badVideoTest.name); +} + +// Returns a Promise that is resolve()d if detect() is rejected. Needs an input +// |element| (e.g. an HTMLImageElement or HTMLVideoElement) and a |url| to load. +function detectOnElementAndExpectError(createDetector, element, url) { + return new Promise((resolve, reject) => { + const tryDetection = async () => { + const detector = createDetector(); + try { + const detectionResult = await detector.detect(element); + reject("Promise should have been rejected."); + } catch (error) { + resolve(error); + } + }; + element.onload = tryDetection; + element.onerror = tryDetection; + element.src = url; + }); +}; + +</script> diff --git a/testing/web-platform/tests/shape-detection/detector-same-object.https.html b/testing/web-platform/tests/shape-detection/detector-same-object.https.html new file mode 100644 index 0000000000..bf7c068041 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/detector-same-object.https.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/shapedetection-helpers.js"></script> +<script> + +// These tests verify that detect()ed Detected{Barcode,Face, Text}'s individual +// fields are [SameObject]. +const imageDataTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + mockTestName: "FaceDetectionTest", + detectionResultTest: CheckDetectedFaceSameObjects, + name: "Face - detect(ImageData), [SameObject]" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + mockTestName: "BarcodeDetectionTest", + detectionResultTest: CheckDetectedBarcodesSameObjects, + name: "Barcode - detect(ImageData), [SameObject]" + }, + { + createDetector: () => { return new TextDetector(); }, + mockTestName: "TextDetectionTest", + detectionResultTest: CheckDetectedTextBlocksSameObjects, + name: "Text - detect(ImageData), [SameObject]", + } + ]; + +for (let imageDataTest of imageDataTests) { + detection_test(imageDataTest.mockTestName, async t => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = "/images/green-16x16.png"; + await imgWatcher.wait_for("load"); + const canvas = document.createElement("canvas"); + canvas.getContext("2d").drawImage(img, 0, 0); + + const detector = imageDataTest.createDetector(); + const detectionResult = await detector.detect(canvas.getContext("2d") + .getImageData(0, 0, canvas.width, canvas.height)); + imageDataTest.detectionResultTest(detectionResult); + }, imageDataTest.name); +} + +function CheckDetectedFaceSameObjects(detectedFaces) { + assert_greater_than(detectedFaces.length, 0, "Number of faces"); + assert_equals(detectedFaces[0].boundingBox, detectedFaces[0].boundingBox); + assert_equals(detectedFaces[0].landmarks, detectedFaces[0].landmarks); +} + +function CheckDetectedBarcodesSameObjects(detectedBarcodes) { + assert_greater_than(detectedBarcodes.length, 0, "Number of barcodes"); + assert_equals(detectedBarcodes[0].rawValue, detectedBarcodes[0].rawValue); + assert_equals(detectedBarcodes[0].boundingBox, detectedBarcodes[0].boundingBox); + assert_equals(detectedBarcodes[0].format, detectedBarcodes[0].format); + assert_equals(detectedBarcodes[0].cornerPoints, detectedBarcodes[0].cornerPoints); +} + +function CheckDetectedTextBlocksSameObjects(detectedTextBlocks) { + assert_greater_than(detectedTextBlocks.length, 0, "Number of textBlocks"); + assert_equals(detectedTextBlocks[0].rawValue, detectedTextBlocks[0].rawValue); + assert_equals(detectedTextBlocks[0].boundingBox, detectedTextBlocks[0].boundingBox); +} + +</script> diff --git a/testing/web-platform/tests/shape-detection/idlharness.https.any.js b/testing/web-platform/tests/shape-detection/idlharness.https.any.js new file mode 100644 index 0000000000..33b5f88f75 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/idlharness.https.any.js @@ -0,0 +1,19 @@ +// META: global=window,worker +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js + +// See: https://wicg.github.io/shape-detection-api/ + +'use strict'; + +idl_test( + ['shape-detection-api', 'text-detection-api'], + ['dom', 'geometry'], + async idl_array => { + idl_array.add_objects({ + FaceDetector: ['new FaceDetector()'], + BarcodeDetector: ['new BarcodeDetector()'], + TextDetector: ['new TextDetector()'], + }); + } +); diff --git a/testing/web-platform/tests/shape-detection/resources/aztec-correction.jpg b/testing/web-platform/tests/shape-detection/resources/aztec-correction.jpg Binary files differnew file mode 100644 index 0000000000..55bc0d23d6 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/aztec-correction.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/aztec-full.jpg b/testing/web-platform/tests/shape-detection/resources/aztec-full.jpg Binary files differnew file mode 100644 index 0000000000..36390f0821 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/aztec-full.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/aztec-layers.jpg b/testing/web-platform/tests/shape-detection/resources/aztec-layers.jpg Binary files differnew file mode 100644 index 0000000000..75449a7044 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/aztec-layers.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/aztec.jpg b/testing/web-platform/tests/shape-detection/resources/aztec.jpg Binary files differnew file mode 100644 index 0000000000..f71e11f064 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/aztec.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/barcodes.mov b/testing/web-platform/tests/shape-detection/resources/barcodes.mov Binary files differnew file mode 100644 index 0000000000..473a7ae239 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/barcodes.mov diff --git a/testing/web-platform/tests/shape-detection/resources/code128-height.jpg b/testing/web-platform/tests/shape-detection/resources/code128-height.jpg Binary files differnew file mode 100644 index 0000000000..69883e5683 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/code128-height.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/code128.jpg b/testing/web-platform/tests/shape-detection/resources/code128.jpg Binary files differnew file mode 100644 index 0000000000..9bb66fe8ca --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/code128.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/face-bottom-left.jpg b/testing/web-platform/tests/shape-detection/resources/face-bottom-left.jpg Binary files differnew file mode 100644 index 0000000000..1fb4e660e4 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/face-bottom-left.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/face-bottom-right.jpg b/testing/web-platform/tests/shape-detection/resources/face-bottom-right.jpg Binary files differnew file mode 100644 index 0000000000..ce8837851b --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/face-bottom-right.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/face-center.jpg b/testing/web-platform/tests/shape-detection/resources/face-center.jpg Binary files differnew file mode 100644 index 0000000000..30364040ed --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/face-center.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/face-top-left.jpg b/testing/web-platform/tests/shape-detection/resources/face-top-left.jpg Binary files differnew file mode 100644 index 0000000000..300866b2eb --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/face-top-left.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/face-top-right.jpg b/testing/web-platform/tests/shape-detection/resources/face-top-right.jpg Binary files differnew file mode 100644 index 0000000000..528a2a778d --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/face-top-right.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/faces.mov b/testing/web-platform/tests/shape-detection/resources/faces.mov Binary files differnew file mode 100644 index 0000000000..b2c7b8c577 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/faces.mov diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-columns.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-columns.jpg Binary files differnew file mode 100644 index 0000000000..54a08cf0ee --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417-columns.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-compact.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-compact.jpg Binary files differnew file mode 100644 index 0000000000..1185c56dfe --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417-compact.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-compaction.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-compaction.jpg Binary files differnew file mode 100644 index 0000000000..20e4a1c64c --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417-compaction.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-correction.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-correction.jpg Binary files differnew file mode 100644 index 0000000000..0c64ea9605 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417-correction.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-rows.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-rows.jpg Binary files differnew file mode 100644 index 0000000000..9f5f67b10c --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417-rows.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-square.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-square.jpg Binary files differnew file mode 100644 index 0000000000..8a379ae6cc --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417-square.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-taller.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-taller.jpg Binary files differnew file mode 100644 index 0000000000..a520f22fde --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417-taller.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417-wider.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417-wider.jpg Binary files differnew file mode 100644 index 0000000000..9f5f67b10c --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417-wider.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/pdf417.jpg b/testing/web-platform/tests/shape-detection/resources/pdf417.jpg Binary files differnew file mode 100644 index 0000000000..ebbe36b510 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/pdf417.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-bottom-left.jpg b/testing/web-platform/tests/shape-detection/resources/qr-bottom-left.jpg Binary files differnew file mode 100644 index 0000000000..754bdedaf1 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-bottom-left.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-bottom-right.jpg b/testing/web-platform/tests/shape-detection/resources/qr-bottom-right.jpg Binary files differnew file mode 100644 index 0000000000..483297100d --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-bottom-right.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-center.jpg b/testing/web-platform/tests/shape-detection/resources/qr-center.jpg Binary files differnew file mode 100644 index 0000000000..ca8e770bf4 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-center.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-h.jpg b/testing/web-platform/tests/shape-detection/resources/qr-h.jpg Binary files differnew file mode 100644 index 0000000000..a1beb02101 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-h.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-l.jpg b/testing/web-platform/tests/shape-detection/resources/qr-l.jpg Binary files differnew file mode 100644 index 0000000000..a29c358393 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-l.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-m.jpg b/testing/web-platform/tests/shape-detection/resources/qr-m.jpg Binary files differnew file mode 100644 index 0000000000..ca8e770bf4 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-m.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-q.jpg b/testing/web-platform/tests/shape-detection/resources/qr-q.jpg Binary files differnew file mode 100644 index 0000000000..45313c71b0 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-q.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-top-left.jpg b/testing/web-platform/tests/shape-detection/resources/qr-top-left.jpg Binary files differnew file mode 100644 index 0000000000..f3092933e4 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-top-left.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/qr-top-right.jpg b/testing/web-platform/tests/shape-detection/resources/qr-top-right.jpg Binary files differnew file mode 100644 index 0000000000..875ee8035c --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/qr-top-right.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/shapedetection-helpers.js b/testing/web-platform/tests/shape-detection/resources/shapedetection-helpers.js new file mode 100644 index 0000000000..1b4949b8f6 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/shapedetection-helpers.js @@ -0,0 +1,75 @@ +'use strict'; + +// These tests rely on the User Agent providing an implementation of +// platform shape detection backends. +// +// In Chromium-based browsers this implementation is provided by a polyfill +// in order to reduce the amount of test-only code shipped to users. To enable +// these tests the browser must be run with these options: +// +// --enable-blink-features=MojoJS,MojoJSTest + +async function loadChromiumResources() { + await import('/resources/chromium/mock-barcodedetection.js'); + await import('/resources/chromium/mock-facedetection.js'); + await import('/resources/chromium/mock-textdetection.js'); +} + +/** + * @param {String} detectionTestName + * name of mock shape detection test interface, + * must be the item of ["FaceDetectionTest", "BarcodeDetectionTest", + * "TextDetectionTest"] +*/ +async function initialize_detection_tests(detectionTestName) { + let detectionTest; + if (typeof document === 'undefined') { + // Use 'self' for workers. + if (typeof self[detectionTestName] === 'undefined') { + // test-only-api.js is already loaded in worker.js + if (isChromiumBased) { + await loadChromiumResources(); + } + } + detectionTest = new self[detectionTestName](); + } else { + if (typeof window[detectionTestName] === 'undefined') { + const script = document.createElement('script'); + script.src = '/resources/test-only-api.js'; + script.async = false; + const p = new Promise((resolve, reject) => { + script.onload = () => { resolve(); }; + script.onerror = e => { reject(e); }; + }) + document.head.appendChild(script); + await p; + + if (isChromiumBased) { + await loadChromiumResources(); + } + + } + detectionTest = new window[detectionTestName](); + } + await detectionTest.initialize(); + return detectionTest; +} + +function detection_test(detectionTestName, func, name, properties) { + promise_test(async t => { + let detectionTest = await initialize_detection_tests(detectionTestName); + try { + await func(t, detectionTest); + } finally { + await detectionTest.reset(); + }; + }, name, properties); +} + +function getArrayBufferFromBigBuffer(bigBuffer) { + if (bigBuffer.bytes !== undefined) { + return new Uint8Array(bigBuffer.bytes).buffer; + } + return bigBuffer.sharedMemory.bufferHandle.mapBuffer(0, + bigBuffer.sharedMemory.size).buffer; +} diff --git a/testing/web-platform/tests/shape-detection/resources/single-detection-helpers.js b/testing/web-platform/tests/shape-detection/resources/single-detection-helpers.js new file mode 100644 index 0000000000..bbd2bda96b --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/single-detection-helpers.js @@ -0,0 +1,66 @@ +function imageLoadedPromise(image) { + return new Promise(function(resolve, reject) { + if (image.complete) + resolve(); + image.addEventListener("load", resolve, { once: true }); + }); +} + +function videoLoadedPromise(video) { + return new Promise(function(resolve, reject) { + if (video.readyState == 4) + resolve(); + else { + video.addEventListener("loadeddata", resolve, { once: true }); + video.addEventListener("error", reject, { once: true }); + } + }); +} + +function waitForNFrames(count) { + if (count <= 0) + return Promise.reject(new TypeError("count should be greater than 0!")); + + return new Promise(resolve => { + function tick() { + (--count) ? requestAnimationFrame(tick) : resolve(); + } + requestAnimationFrame(tick); + }); +} + +function seekTo(video, time) { + return new Promise(function(resolve, reject) { + video.addEventListener("seeked", async function() { + /* Work around flakiness in video players... */ + await waitForNFrames(3); + resolve(); + }, { once: true }); + video.currentTime = time; + }); +} + +function checkBoundingBox(actual, expected, fuzziness) { + assert_equals(actual.constructor.name, "DOMRectReadOnly"); + assert_approx_equals(actual.left, expected.left, fuzziness); + assert_approx_equals(actual.right, expected.right, fuzziness); + assert_approx_equals(actual.top, expected.top, fuzziness); + assert_approx_equals(actual.bottom, expected.bottom, fuzziness); +} + +function checkPointsLieWithinBoundingBox(points, boundingBox) { + for (point of points) { + assert_between_inclusive(point.x, boundingBox.left, boundingBox.right); + assert_between_inclusive(point.y, boundingBox.top, boundingBox.bottom); + } +} + +function checkPointIsNear(actual, expected, fuzzinessX, fuzzinessY) { + assert_approx_equals(actual.x, expected.x, fuzzinessX); + assert_approx_equals(actual.y, expected.y, fuzzinessY); +} + +function checkPointsAreNear(actual, expected, fuzzinessX, fuzzinessY) { + for (point of actual) + checkPointIsNear(point, expected, fuzzinessX, fuzzinessY); +} diff --git a/testing/web-platform/tests/shape-detection/resources/text-bottom-left.jpg b/testing/web-platform/tests/shape-detection/resources/text-bottom-left.jpg Binary files differnew file mode 100644 index 0000000000..12a0641fd3 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/text-bottom-left.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/text-bottom-right.jpg b/testing/web-platform/tests/shape-detection/resources/text-bottom-right.jpg Binary files differnew file mode 100644 index 0000000000..7ad8b71b6c --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/text-bottom-right.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/text-center.jpg b/testing/web-platform/tests/shape-detection/resources/text-center.jpg Binary files differnew file mode 100644 index 0000000000..69e8ce446c --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/text-center.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/text-top-left.jpg b/testing/web-platform/tests/shape-detection/resources/text-top-left.jpg Binary files differnew file mode 100644 index 0000000000..53ca0e0aaf --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/text-top-left.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/text-top-right.jpg b/testing/web-platform/tests/shape-detection/resources/text-top-right.jpg Binary files differnew file mode 100644 index 0000000000..76a22a677c --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/text-top-right.jpg diff --git a/testing/web-platform/tests/shape-detection/resources/text.mov b/testing/web-platform/tests/shape-detection/resources/text.mov Binary files differnew file mode 100644 index 0000000000..d50197b326 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/resources/text.mov diff --git a/testing/web-platform/tests/shape-detection/shapedetection-cross-origin.sub.https.html b/testing/web-platform/tests/shape-detection/shapedetection-cross-origin.sub.https.html new file mode 100644 index 0000000000..c4e3c3fec7 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/shapedetection-cross-origin.sub.https.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> + +// cross-origin resources +const IMAGE_URL = + "https://{{domains[www1]}}:{{ports[https][0]}}/images/green.png"; +const VIDEO_URL = + "https://{{domains[www1]}}:{{ports[https][0]}}/media/white.webm"; + +const crossOriginTests = + [ + { + createDetector: () => { return new FaceDetector(); }, + detectorType: "FaceDetector" + }, + { + createDetector: () => { return new BarcodeDetector(); }, + detectorType: "BarcodeDetector" + }, + { + createDetector: () => { return new TextDetector(); }, + detectorType: "TextDetector" + } + ]; + +for (let crossOriginTest of crossOriginTests) { + + // Verifies that Detector rejects a cross-origin HTMLImageElement. + promise_test(async t => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = IMAGE_URL; + await imgWatcher.wait_for("load"); + const detector = crossOriginTest.createDetector(); + promise_rejects_dom(t, "SecurityError", detector.detect(img)); + }, crossOriginTest.detectorType + + " should reject cross-origin HTMLImageElements with a SecurityError."); + + // Verifies that Detector rejects a cross-origin ImageBitmap. + promise_test(async t => { + const img = new Image(); + const imgWatcher = new EventWatcher(t, img, ["load", "error"]); + img.src = IMAGE_URL; + await imgWatcher.wait_for("load"); + const imgBitmap = await createImageBitmap(img); + const detector = crossOriginTest.createDetector(); + promise_rejects_dom(t, "SecurityError", detector.detect(imgBitmap)); + }, crossOriginTest.detectorType + + " should reject cross-origin ImageBitmaps with a SecurityError."); + + // Verifies that Detector rejects a cross-origin HTMLVideoElement. + promise_test(async t => { + const video = document.createElement('video'); + const videoWatcher = new EventWatcher(t, video, ["loadeddata", "error"]); + video.src = VIDEO_URL; + await videoWatcher.wait_for("loadeddata"); + const detector = crossOriginTest.createDetector(); + promise_rejects_dom(t, "SecurityError", detector.detect(video)); + }, crossOriginTest.detectorType + + " should reject cross-origin HTMLVideoElements with a SecurityError."); + +} + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/shape-detection/single-barcode-detection.https.html b/testing/web-platform/tests/shape-detection/single-barcode-detection.https.html new file mode 100644 index 0000000000..1f14e35153 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/single-barcode-detection.https.html @@ -0,0 +1,396 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/single-detection-helpers.js"></script> +<body> +</body> +<script> +const imageTests = { + aztecCorrection: { + name: "aztec-correction.jpg", + format: "aztec", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 240, right: 559, top: 144, bottom: 454}, fuzziness: 15}, + topLeft: {position: {x: 240, y: 144}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 559, y: 144}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 559, y: 454}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 240, y: 454}, fuzzinessX: 15, fuzzinessY: 15}}, + aztecFull: { + name: "aztec-full.jpg", + format: "aztec", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 281, right: 518, top: 184, bottom: 414}, fuzziness: 15}, + topLeft: {position: {x: 281, y: 184}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 518, y: 184}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 518, y: 414}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 281, y: 414}, fuzzinessX: 15, fuzzinessY: 15}}, + aztecLayers: { + name: "aztec-layers.jpg", + format: "aztec", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 175, right: 625, top: 75, bottom: 525}, fuzziness: 15}, + topLeft: {position: {x: 175, y: 75}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 625, y: 75}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 625, y: 525}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 175, y: 525}, fuzzinessX: 15, fuzzinessY: 15}}, + aztec: { + name: "aztec.jpg", + format: "aztec", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 302, right: 497, top: 202, bottom: 397}, fuzziness: 15}, + topLeft: {position: {x: 302, y: 202}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 497, y: 202}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 497, y: 397}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 302, y: 397}, fuzzinessX: 15, fuzzinessY: 15}}, + code128Height: { + name: "code128-height.jpg", + format: "code_128", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 90, right: 711, top: 149, bottom: 449}, fuzziness: 15}, + topLeft: {position: {x: 90, y: 149}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 711, y: 149}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 711, y: 450}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 90, y: 450}, fuzzinessX: 15, fuzzinessY: 15}}, + code128: { + name: "code128.jpg", + format: "code_128", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 90, right: 710, top: 267, bottom: 332}, fuzziness: 15}, + topLeft: {position: {x: 90, y: 267}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 710, y: 267}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 710, y: 332}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 90, y: 332}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417Columns: { + name: "pdf417-columns.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 39, right: 755, top: 243, bottom: 356}, fuzziness: 15}, + topLeft: {position: {x: 39, y: 243}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 755, y: 243}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 755, y: 356}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 39, y: 356}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417Compact: { + name: "pdf417-compact.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 186, right: 786, top: 242, bottom: 359}, fuzziness: 15}, + topLeft: {position: {x: 186, y: 242}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 786, y: 242}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 786, y: 359}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 186, y: 359}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417Compaction: { + name: "pdf417-compaction.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 84, right: 712, top: 217, bottom: 382}, fuzziness: 15}, + topLeft: {position: {x: 84, y: 217}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 712, y: 217}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 712, y: 382}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 84, y: 382}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417Correction: { + name: "pdf417-correction.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 39, right: 755, top: 209, bottom: 390}, fuzziness: 15}, + topLeft: {position: {x: 39, y: 209}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 755, y: 209}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 755, y: 390}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 39, y: 390}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417Rows: { + name: "pdf417-rows.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 84, right: 712, top: 227, bottom: 360}, fuzziness: 15}, + topLeft: {position: {x: 84, y: 227}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 712, y: 227}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 712, y: 360}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 84, y: 360}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417Square: { + name: "pdf417-square.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 170, right: 621, top: 119, bottom: 480}, fuzziness: 15}, + topLeft: {position: {x: 171, y: 119}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 621, y: 119}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 621, y: 480}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 170, y: 480}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417Taller: { + name: "pdf417-taller.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 84, right: 713, top: 209, bottom: 390}, fuzziness: 15}, + topLeft: {position: {x: 84, y: 209}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 713, y: 209}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 713, y: 390}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 84, y: 390}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417Wider: { + name: "pdf417-wider.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 84, right: 712, top: 227, bottom: 360}, fuzziness: 15}, + topLeft: {position: {x: 84, y: 227}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 712, y: 227}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 712, y: 360}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 84, y: 360}, fuzzinessX: 15, fuzzinessY: 15}}, + pdf417: { + name: "pdf417.jpg", + format: "pdf417", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 126, right: 666, top: 209, bottom: 390}, fuzziness: 15}, + topLeft: {position: {x: 126, y: 209}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 666, y: 209}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 666, y: 390}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 126, y: 390}, fuzzinessX: 15, fuzzinessY: 15}}, + qrBottomLeft: { + name: "qr-bottom-left.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 75, right: 325, top: 325, bottom: 575}, fuzziness: 5}, + topLeft: {position: {x: 75, y: 325}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 325, y: 325}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 325, y: 575}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 75, y: 575}, fuzzinessX: 15, fuzzinessY: 15}}, + qrBottomRight: { + name: "qr-bottom-right.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 475, right: 725, top: 325, bottom: 575}, fuzziness: 5}, + topLeft: {position: {x: 475, y: 325}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 725, y: 325}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 725, y: 575}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 475, y: 575}, fuzzinessX: 15, fuzzinessY: 15}}, + qrCenter: { + name: "qr-center.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 277, right: 524, top: 172, bottom: 428}, fuzziness: 5}, + topLeft: {position: {x: 277, y: 172}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 521, y: 172}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 524, y: 425}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 277, y: 428}, fuzzinessX: 15, fuzzinessY: 15}}, + qrH: { + name: "qr-h.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 235, right: 565, top: 135, bottom: 465}, fuzziness: 5}, + topLeft: {position: {x: 235, y: 135}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 565, y: 135}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 565, y: 465}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 235, y: 465}, fuzzinessX: 15, fuzzinessY: 15}}, + qrL: { + name: "qr-l.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 275, right: 525, top: 175, bottom: 425}, fuzziness: 5}, + topLeft: {position: {x: 275, y: 175}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 525, y: 175}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 525, y: 425}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 275, y: 425}, fuzzinessX: 15, fuzzinessY: 15}}, + qrM: { + name: "qr-m.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 277, right: 524, top: 172, bottom: 428}, fuzziness: 5}, + topLeft: {position: {x: 277, y: 172}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 521, y: 172}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 524, y: 425}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 277, y: 428}, fuzzinessX: 15, fuzzinessY: 15}}, + qrQ: { + name: "qr-q.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 252, right: 548, top: 157, bottom: 444}, fuzziness: 5}, + topLeft: {position: {x: 252, y: 157}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 548, y: 157}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 545, y: 444}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 252, y: 441}, fuzzinessX: 15, fuzzinessY: 15}}, + qrTopLeft: { + name: "qr-top-left.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 75, right: 325, top: 25, bottom: 275}, fuzziness: 5}, + topLeft: {position: {x: 75, y: 25}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 325, y: 25}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 325, y: 275}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 75, y: 275}, fuzzinessX: 15, fuzzinessY: 15}}, + qrTopRight: { + name: "qr-top-right.jpg", + format: "qr_code", + payload: "Barcode Detection is Fun!", + barcode: {boundingBox: {left: 475, right: 725, top: 25, bottom: 275}, fuzziness: 5}, + topLeft: {position: {x: 475, y: 25}, fuzzinessX: 15, fuzzinessY: 15}, + topRight: {position: {x: 725, y: 25}, fuzzinessX: 15, fuzzinessY: 15}, + bottomRight: {position: {x: 725, y: 275}, fuzzinessX: 15, fuzzinessY: 15}, + bottomLeft: {position: {x: 475, y: 275}, fuzzinessX: 15, fuzzinessY: 15}}}; + +const videoTests = { + "barcodes.mov": [ + {time: 0.5, test: imageTests.aztecCorrection}, + {time: 1.5, test: imageTests.aztecFull}, + {time: 2.5, test: imageTests.aztecLayers}, + {time: 3.5, test: imageTests.aztec}, + {time: 4.5, test: imageTests.code128Height}, + {time: 5.5, test: imageTests.code128}, + {time: 6.5, test: imageTests.pdf417Columns}, + {time: 7.5, test: imageTests.pdf417Compact}, + {time: 8.5, test: imageTests.pdf417Compaction}, + {time: 9.5, test: imageTests.pdf417Correction}, + {time: 10.5, test: imageTests.pdf417Rows}, + {time: 11.5, test: imageTests.pdf417Square}, + {time: 12.5, test: imageTests.pdf417Taller}, + {time: 13.5, test: imageTests.pdf417Wider}, + {time: 14.5, test: imageTests.pdf417}, + {time: 15.5, test: imageTests.qrBottomLeft}, + {time: 16.5, test: imageTests.qrBottomRight}, + {time: 17.5, test: imageTests.qrCenter}, + {time: 18.5, test: imageTests.qrH}, + {time: 19.5, test: imageTests.qrL}, + {time: 20.5, test: imageTests.qrM}, + {time: 21.5, test: imageTests.qrQ}, + {time: 22.5, test: imageTests.qrTopLeft}, + {time: 23.5, test: imageTests.qrTopRight}]}; + +// All the fields in FaceDetectorOptions are hints, so they can't be tested. +const barcodeDetector = new BarcodeDetector(); + +async function testImage(imageBitmapSource, test, key) { + const supportedFormats = await BarcodeDetector.getSupportedFormats(); + if (!supportedFormats.includes(test.format)) + return; + const detectedBarcodes = await barcodeDetector.detect(imageBitmapSource); + assert_equals(detectedBarcodes.length, 1); + const detectedBarcode = detectedBarcodes[0]; + checkBoundingBox(detectedBarcode.boundingBox, test.barcode.boundingBox, test.barcode.fuzziness); + assert_equals(detectedBarcode.rawValue, test.payload); + assert_equals(detectedBarcode.format, test.format); + assert_equals(detectedBarcode.cornerPoints.length, 4); + const [topLeft, topRight, bottomRight, bottomLeft] = detectedBarcode.cornerPoints; + checkPointIsNear(topLeft, test.topLeft.position, test.topLeft.fuzzinessX, test.topLeft.fuzzinessY); + checkPointIsNear(topRight, test.topRight.position, test.topRight.fuzzinessX, test.topRight.fuzzinessY); + checkPointIsNear(bottomRight, test.bottomRight.position, test.bottomRight.fuzzinessX, test.bottomRight.fuzzinessY); + checkPointIsNear(bottomLeft, test.bottomLeft.position, test.bottomLeft.fuzzinessX, test.bottomLeft.fuzzinessY); +} + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + await testImage(imageElement, imageTest, key); + } +}, "HTMLImageElement"); + +// Intentionally don't test SVGImageElement. The spec https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesource says it's supposed to be +// a CanvasImageSource, but neither WebKit nor Blink actually seem to implement that. + +promise_test(async t => { + for (const [name, tests] of Object.entries(videoTests)) { + const videoElement = document.createElement("video"); + document.body.appendChild(videoElement); + videoElement.src = `resources/${name}`; + const loadedPromise = videoLoadedPromise(videoElement); + videoElement.load(); + await loadedPromise; + for (const test of tests) { + await seekTo(videoElement, test.time); + await testImage(videoElement, test.test, name); + } + document.body.removeChild(videoElement); + } +}, "HTMLVideoElement"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + await testImage(canvasElement, imageTest, key); + } +}, "HTMLCanvasElement"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const imageBitmap = await createImageBitmap(imageElement); + await testImage(imageBitmap, imageTest, key); + } +}, "ImageBitmap"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const offscreenCanvas = new OffscreenCanvas(imageElement.width, imageElement.height); + const context = offscreenCanvas.getContext("2d"); + context.drawImage(imageElement, 0, 0); + await testImage(offscreenCanvas, imageTest, key); + } +}, "OffscreenCanvas"); + +promise_test(async t => { + for (const [name, tests] of Object.entries(videoTests)) { + const videoElement = document.createElement("video"); + document.body.appendChild(videoElement); + videoElement.src = `resources/${name}`; + const loadedPromise = videoLoadedPromise(videoElement); + videoElement.load(); + await loadedPromise; + for (const test of tests) { + await seekTo(videoElement, test.time); + const videoFrame = new VideoFrame(videoElement); + await testImage(videoFrame, test.test, name); + videoFrame.close(); + } + document.body.removeChild(videoElement); + } +}, "VideoFrame"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + const blob = await new Promise(function(resolve, reject) { + canvasElement.toBlob(function(blob) { + return resolve(blob); + }); + }); + await testImage(blob, imageTest, key); + } +}, "Blob"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + const imageData = context.getImageData(0, 0, canvasElement.width, canvasElement.height); + await testImage(imageData, imageTest, key); + } +}, "ImageData"); + +</script> diff --git a/testing/web-platform/tests/shape-detection/single-face-detection.https.html b/testing/web-platform/tests/shape-detection/single-face-detection.https.html new file mode 100644 index 0000000000..28afdef136 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/single-face-detection.https.html @@ -0,0 +1,223 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/single-detection-helpers.js"></script> +<body> +</body> +<script> +const imageTests = { + center: { + name: "face-center.jpg", + face: {boundingBox: {left: 312, right: 512, top: 238, bottom: 438}, fuzziness: 25}, + mouth: {position: {x: 414, y: 379}, fuzzinessX: 30, fuzzinessY: 20}, + leftEye: {position: {x: 378, y: 293}, fuzzinessX: 20, fuzzinessY: 10}, + rightEye: {position: {x: 448, y: 292}, fuzzinessX: 20, fuzzinessY: 10}, + nose: {position: {x: 412, y: 335}, fuzzinessX: 20, fuzzinessY: 35}}, + bottomLeft: { + name: "face-bottom-left.jpg", + face: {boundingBox: {left: 96, right: 387, top: 281, bottom: 572}, fuzziness: 15}, + mouth: {position: {x: 248, y: 483}, fuzzinessX: 45, fuzzinessY: 25}, + leftEye: {position: {x: 196, y: 359}, fuzzinessX: 25, fuzzinessY: 10}, + rightEye: {position: {x: 296, y: 357}, fuzzinessX: 25, fuzzinessY: 10}, + nose: {position: {x: 244, y: 419}, fuzzinessX: 30, fuzzinessY: 50}}, + bottomRight: { + name: "face-bottom-right.jpg", + face: {boundingBox: {left: 445, right: 733, top: 284, bottom: 572}, fuzziness: 10}, + mouth: {position: {x: 593, y: 487}, fuzzinessX: 45, fuzzinessY: 25}, + leftEye: {position: {x: 542, y: 363}, fuzzinessX: 25, fuzzinessY: 10}, + rightEye: {position: {x: 641, y: 361}, fuzzinessX: 25, fuzzinessY: 10}, + nose: {position: {x: 590, y: 423}, fuzzinessX: 30, fuzzinessY: 50}}, + topLeft: { + name: "face-top-left.jpg", + face: {boundingBox: {left: 101, right: 387, top: 119, bottom: 405}, fuzziness: 10}, + mouth: {position: {x: 246, y: 322}, fuzzinessX: 45, fuzzinessY: 25}, + leftEye: {position: {x: 194, y: 198}, fuzzinessX: 25, fuzzinessY: 10}, + rightEye: {position: {x: 295, y: 196}, fuzzinessX: 25, fuzzinessY: 10}, + nose: {position: {x: 243, y: 258}, fuzzinessX: 30, fuzzinessY: 50}}, + topRight: { + name: "face-top-right.jpg", + face: {boundingBox: {left: 451, right: 735, top: 124, bottom: 408}, fuzziness: 10}, + mouth: {position: {x: 594, y: 326}, fuzzinessX: 45, fuzzinessY: 25}, + leftEye: {position: {x: 542, y: 202}, fuzzinessX: 25, fuzzinessY: 10}, + rightEye: {position: {x: 642, y: 200}, fuzzinessX: 25, fuzzinessY: 10}, + nose: {position: {x: 591, y: 261}, fuzzinessX: 30, fuzzinessY: 50}}}; + +const videoTests = { + "faces.mov": [ + {time: 0.5, test: imageTests.center}, + {time: 1.5, test: imageTests.bottomLeft}, + {time: 2.5, test: imageTests.bottomRight}, + {time: 3.5, test: imageTests.topLeft}, + {time: 4.5, test: imageTests.topRight}]}; + +// All the fields in FaceDetectorOptions are hints, so they can't be tested. +const faceDetector = new FaceDetector(); + +async function testImage(imageBitmapSource, test) { + const detectedFaces = await faceDetector.detect(imageBitmapSource); + assert_equals(detectedFaces.length, 1); + const detectedFace = detectedFaces[0]; + checkBoundingBox(detectedFace.boundingBox, test.face.boundingBox, test.face.fuzziness); + if (detectedFace.landmarks) { + var mouthCount = 0; + var eyeCount = 0; + var noseCount = 0; + for (landmark of detectedFace.landmarks) { + checkPointsLieWithinBoundingBox(landmark.locations, detectedFace.boundingBox); + switch (landmark.type) { + case "mouth": + checkPointsAreNear(landmark.locations, test.mouth.position, test.mouth.fuzzinessX, test.mouth.fuzzinessY); + ++mouthCount; + break; + case "eye": + // handled below + ++eyeCount; + break; + case "nose": + checkPointsAreNear(landmark.locations, test.nose.position, test.nose.fuzzinessX, test.nose.fuzzinessY); + ++noseCount; + break; + default: + assert(false); + } + } + assert_less_than_equal(mouthCount, 1); + assert_true(eyeCount == 0 || eyeCount == 2, "There should be 2 eyes (or the implementation doesn't support detecting eyes)"); + assert_less_than_equal(noseCount, 1); + + const [leftEye, rightEye] = detectedFace.landmarks.filter(landmark => landmark.type == "eye").toSorted(function(landmarkA, landmarkB) { + // The left eye has a smaller X coordinate than the right eye. + const locationsA = landmarkA.locations.map(location => location.x); + const locationsB = landmarkB.locations.map(location => location.x); + const locationA = locationsA.reduce((a, b) => a + b) / locationsA.length; + const locationB = locationsB.reduce((a, b) => a + b) / locationsB.length; + return locationA - locationB; + }); + checkPointsAreNear(leftEye.locations, test.leftEye.position, test.leftEye.fuzzinessX, test.leftEye.fuzzinessY); + checkPointsAreNear(rightEye.locations, test.rightEye.position, test.rightEye.fuzzinessX, test.rightEye.fuzzinessY); + } +} + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + await testImage(imageElement, imageTest); + } +}, "HTMLImageElement"); + +// Intentionally don't test SVGImageElement. The spec https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesource says it's supposed to be +// a CanvasImageSource, but neither WebKit nor Blink actually seem to implement that. + +promise_test(async t => { + for (const [name, tests] of Object.entries(videoTests)) { + const videoElement = document.createElement("video"); + document.body.appendChild(videoElement); + videoElement.src = `resources/${name}`; + const loadedPromise = videoLoadedPromise(videoElement); + videoElement.load(); + await loadedPromise; + for (const test of tests) { + await seekTo(videoElement, test.time); + await testImage(videoElement, test.test); + } + document.body.removeChild(videoElement); + } +}, "HTMLVideoElement"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + await testImage(canvasElement, imageTest); + } +}, "HTMLCanvasElement"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const imageBitmap = await createImageBitmap(imageElement); + await testImage(imageBitmap, imageTest); + } +}, "ImageBitmap"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const offscreenCanvas = new OffscreenCanvas(imageElement.width, imageElement.height); + const context = offscreenCanvas.getContext("2d"); + context.drawImage(imageElement, 0, 0); + await testImage(offscreenCanvas, imageTest); + } +}, "OffscreenCanvas"); + +promise_test(async t => { + for (const [name, tests] of Object.entries(videoTests)) { + const videoElement = document.createElement("video"); + document.body.appendChild(videoElement); + videoElement.src = `resources/${name}`; + const loadedPromise = videoLoadedPromise(videoElement); + videoElement.load(); + await loadedPromise; + for (const test of tests) { + await seekTo(videoElement, test.time); + const videoFrame = new VideoFrame(videoElement); + await testImage(videoFrame, test.test); + videoFrame.close(); + } + document.body.removeChild(videoElement); + } +}, "VideoFrame"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + const blob = await new Promise(function(resolve, reject) { + canvasElement.toBlob(function(blob) { + return resolve(blob); + }); + }); + await testImage(blob, imageTest); + } +}, "Blob"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_true(imageElement.complete, "Image element should have loaded successfully"); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + const imageData = context.getImageData(0, 0, canvasElement.width, canvasElement.height); + await testImage(imageData, imageTest); + } +}, "ImageData"); + +</script> diff --git a/testing/web-platform/tests/shape-detection/single-text-detection.https.html b/testing/web-platform/tests/shape-detection/single-text-detection.https.html new file mode 100644 index 0000000000..829e8ce6d6 --- /dev/null +++ b/testing/web-platform/tests/shape-detection/single-text-detection.https.html @@ -0,0 +1,197 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/single-detection-helpers.js"></script> +<body> +</body> +<script> +const imageTests = { + center: { + name: "text-center.jpg", + value: "Dictionary", + text: {boundingBox: {left: 195, right: 613, top: 258, bottom: 358}, fuzziness: 7}, + topLeft: {position: {x: 199, y: 258}, fuzzinessX: 10, fuzzinessY: 10}, + topRight: {position: {x: 613, y: 281}, fuzzinessX: 10, fuzzinessY: 17}, + bottomRight: {position: {x: 609, y: 358}, fuzzinessX: 10, fuzzinessY: 4}, + bottomLeft: {position: {x: 195, y: 334}, fuzzinessX: 10, fuzzinessY: 22}}, + bottomLeft: { + name: "text-bottom-left.jpg", + value: "Dictionary", + text: {boundingBox: {left: 53, right: 469, top: 461, bottom: 546}, fuzziness: 10}, + topLeft: {position: {x: 53, y: 461}, fuzzinessX: 10, fuzzinessY: 20}, + topRight: {position: {x: 469, y: 463}, fuzzinessX: 10, fuzzinessY: 20}, + bottomRight: {position: {x: 469, y: 546}, fuzzinessX: 10, fuzzinessY: 17}, + bottomLeft: {position: {x: 53, y: 544}, fuzzinessX: 10, fuzzinessY: 25}}, + bottomRight: { + name: "text-bottom-right.jpg", + value: "Dictionary", + text: {boundingBox: {left: 357, right: 772, top: 471, bottom: 564}, fuzziness: 10}, + topLeft: {position: {x: 358, y: 471}, fuzzinessX: 10, fuzzinessY: 20}, + topRight: {position: {x: 772, y: 476}, fuzzinessX: 10, fuzzinessY: 20}, + bottomRight: {position: {x: 771, y: 564}, fuzzinessX: 10, fuzzinessY: 17}, + bottomLeft: {position: {x: 357, y: 559}, fuzzinessX: 10, fuzzinessY: 25}}, + topLeft: { + name: "text-top-left.jpg", + value: "Dictionary", + text: {boundingBox: {left: 53, right: 474, top: 81, bottom: 182}, fuzziness: 10}, + topLeft: {position: {x: 58, y: 81}, fuzzinessX: 10, fuzzinessY: 20}, + topRight: {position: {x: 474, y: 105}, fuzzinessX: 10, fuzzinessY: 20}, + bottomRight: {position: {x: 470, y: 182}, fuzzinessX: 10, fuzzinessY: 17}, + bottomLeft: {position: {x: 53, y: 158}, fuzzinessX: 10, fuzzinessY: 25}}, + topRight: { + name: "text-top-right.jpg", + value: "Dictionary", + text: {boundingBox: {left: 343, right: 761, top: 66, bottom: 146}, fuzziness: 10}, + topLeft: {position: {x: 343, y: 66}, fuzzinessX: 10, fuzzinessY: 20}, + topRight: {position: {x: 761, y: 69}, fuzzinessX: 10, fuzzinessY: 20}, + bottomRight: {position: {x: 761, y: 146}, fuzzinessX: 10, fuzzinessY: 17}, + bottomLeft: {position: {x: 343, y: 143}, fuzzinessX: 10, fuzzinessY: 25}}}; + +const videoTests = { + "text.mov": [ + {time: 0.5, test: imageTests.center}, + {time: 1.5, test: imageTests.bottomLeft}, + {time: 2.5, test: imageTests.bottomRight}, + {time: 3.5, test: imageTests.topLeft}, + {time: 4.5, test: imageTests.topRight}]}; + +// The TextDetector contructor doesn't take any options. +const textDetector = new TextDetector(); + +async function testImage(imageBitmapSource, test) { + var detectedText = await textDetector.detect(imageBitmapSource); + assert_equals(detectedText.length, 1); + detectedText = detectedText[0]; + assert_equals(detectedText.rawValue, test.value); + checkBoundingBox(detectedText.boundingBox, test.text.boundingBox, test.text.fuzziness); + assert_equals(detectedText.cornerPoints.length, 4); + const [topLeft, topRight, bottomRight, bottomLeft] = detectedText.cornerPoints; + checkPointIsNear(topLeft, test.topLeft.position, test.topLeft.fuzzinessX, test.topLeft.fuzzinessY); + checkPointIsNear(topRight, test.topRight.position, test.topRight.fuzzinessX, test.topRight.fuzzinessY); + checkPointIsNear(bottomRight, test.bottomRight.position, test.bottomRight.fuzzinessX, test.bottomRight.fuzzinessY); + checkPointIsNear(bottomLeft, test.bottomLeft.position, test.bottomLeft.fuzzinessX, test.bottomLeft.fuzzinessY); +} + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_equals(imageElement.complete, true); + await testImage(imageElement, imageTest); + } +}, "HTMLImageElement"); + +// Intentionally don't test SVGImageElement. The spec https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesource says it's supposed to be +// a CanvasImageSource, but neither WebKit nor Blink actually seem to implement that. + +promise_test(async t => { + for (const [name, tests] of Object.entries(videoTests)) { + const videoElement = document.createElement("video"); + document.body.appendChild(videoElement); + videoElement.src = `resources/${name}`; + const loadedPromise = videoLoadedPromise(videoElement); + videoElement.load(); + await loadedPromise; + for (const test of tests) { + await seekTo(videoElement, test.time); + await testImage(videoElement, test.test); + } + document.body.removeChild(videoElement); + } +}, "HTMLVideoElement"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_equals(imageElement.complete, true); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + await testImage(canvasElement, imageTest); + } +}, "HTMLCanvasElement"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_equals(imageElement.complete, true); + const imageBitmap = await createImageBitmap(imageElement); + await testImage(imageBitmap, imageTest); + } +}, "ImageBitmap"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_equals(imageElement.complete, true); + const offscreenCanvas = new OffscreenCanvas(imageElement.width, imageElement.height); + const context = offscreenCanvas.getContext("2d"); + context.drawImage(imageElement, 0, 0); + await testImage(offscreenCanvas, imageTest); + } +}, "OffscreenCanvas"); + +promise_test(async t => { + for (const [name, tests] of Object.entries(videoTests)) { + const videoElement = document.createElement("video"); + document.body.appendChild(videoElement); + videoElement.src = `resources/${name}`; + const loadedPromise = videoLoadedPromise(videoElement); + videoElement.load(); + await loadedPromise; + for (const test of tests) { + await seekTo(videoElement, test.time); + const videoFrame = new VideoFrame(videoElement); + await testImage(videoFrame, test.test); + videoFrame.close(); + } + document.body.removeChild(videoElement); + } +}, "VideoFrame"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_equals(imageElement.complete, true); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + const blob = await new Promise(function(resolve, reject) { + canvasElement.toBlob(function(blob) { + return resolve(blob); + }); + }); + await testImage(blob, imageTest); + } +}, "Blob"); + +promise_test(async t => { + for (const [key, imageTest] of Object.entries(imageTests)) { + const imageElement = document.createElement("img"); + imageElement.src = `resources/${imageTest.name}`; + await imageLoadedPromise(imageElement); + assert_equals(imageElement.complete, true); + const canvasElement = document.createElement("canvas"); + canvasElement.width = imageElement.width; + canvasElement.height = imageElement.height; + const context = canvasElement.getContext("2d"); + context.drawImage(imageElement, 0, 0); + const imageData = context.getImageData(0, 0, canvasElement.width, canvasElement.height); + await testImage(imageData, imageTest); + } +}, "ImageData"); + +</script> |