summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webnn
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
commit8dd16259287f58f9273002717ec4d27e97127719 (patch)
tree3863e62a53829a84037444beab3abd4ed9dfc7d0 /testing/web-platform/tests/webnn
parentReleasing progress-linux version 126.0.1-1~progress7.99u1. (diff)
downloadfirefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz
firefox-8dd16259287f58f9273002717ec4d27e97127719.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webnn')
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/arg_min_max.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/batch_normalization.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js17
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/cast.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/clamp.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/compute-arraybufferview-with-bigger-arraybuffer.https.any.js61
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/concat.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/constant.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/conv2d.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/conv_transpose2d.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/elementwise_binary.https.any.js6
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/elementwise_logical.https.any.js27
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/elementwise_unary.https.any.js12
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/elu.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/expand.https.any.js6
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gather.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gemm.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/arg_min_max.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/batch_normalization.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/buffer.https.any.js16
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/cast.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/clamp.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/concat.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/constant.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/conv2d.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/conv_transpose2d.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_binary.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_logical.https.any.js20
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_unary.https.any.js13
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/elu.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/expand.https.any.js11
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/gather.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/gemm.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/hard_sigmoid.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/hard_swish.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/instance_normalization.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/layer_normalization.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/leaky_relu.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/linear.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/matmul.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/pad.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/pooling.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/prelu.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/reduction.https.any.js24
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/relu.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/resample2d.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/reshape.https.any.js11
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/sigmoid.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/slice.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/softmax.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/softplus.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/softsign.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/split.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/tanh.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/transpose.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/triangular.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/gpu/where.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/hard_sigmoid.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/hard_swish.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/instance_normalization.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/layer_normalization.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/leaky_relu.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/linear.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/matmul.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/pad.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/parallel-compute.https.any.js19
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/pooling.https.any.js5
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/prelu.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/reduction.https.any.js31
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/relu.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/resample2d.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/reshape.https.any.js5
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/sigmoid.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/slice.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/softmax.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/softplus.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/softsign.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/split.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/tanh.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/transpose.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/triangular.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/conformance_tests/where.https.any.js4
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/arg_max.json2
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/arg_min.json2
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/matmul.json384
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/prelu.json28
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_l1.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_l2.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum_exp.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_max.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_mean.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_min.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_product.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_sum.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/reduce_sum_square.json8
-rw-r--r--testing/web-platform/tests/webnn/resources/test_data/softplus.json152
-rw-r--r--testing/web-platform/tests/webnn/resources/utils.js140
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js53
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js475
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js470
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/elu.https.any.js40
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/expand.https.any.js63
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/gelu.https.any.js10
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js140
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js28
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js149
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js180
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js28
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/linear.https.any.js28
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js113
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/pad.https.any.js70
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/pooling-and-reduction-keep-dims.https.any.js94
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js65
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/slice.https.any.js66
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js3
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/split.https.any.js80
-rw-r--r--testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js51
118 files changed, 2652 insertions, 1094 deletions
diff --git a/testing/web-platform/tests/webnn/conformance_tests/arg_min_max.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/arg_min_max.https.any.js
index 123c8b1048..0f9e590fc8 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/arg_min_max.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/arg_min_max.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API argMin/Max operations
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-argminmax
-testWebNNOperation(['argMin', 'argMax'], buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests(['argMin', 'argMax'], buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/batch_normalization.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/batch_normalization.https.any.js
index 9a1c85db19..d3107820db 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/batch_normalization.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/batch_normalization.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API batchNormalization operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-batchnorm
-testWebNNOperation('batchNormalization', buildBatchNorm); \ No newline at end of file
+runWebNNConformanceTests('batchNormalization', buildBatchNorm);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js
index 9391be8dbf..5a09b05c7d 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API buffer operations
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,10 +9,11 @@
// https://webmachinelearning.github.io/webnn/#api-mlbuffer
-testCreateWebNNBuffer("create", 4);
-
-testDestroyWebNNBuffer('destroyTwice');
-
-testReadWebNNBuffer('read');
-
-testWriteWebNNBuffer('write');
+if (navigator.ml) {
+ testCreateWebNNBuffer('create', 4);
+ testDestroyWebNNBuffer('destroyTwice');
+ testReadWebNNBuffer('read');
+ testWriteWebNNBuffer('write');
+} else {
+ test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
+}
diff --git a/testing/web-platform/tests/webnn/conformance_tests/cast.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/cast.https.any.js
index bde2b9a4ce..086428dd96 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/cast.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/cast.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API cast operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-cast
-testWebNNOperation('cast', buildCast); \ No newline at end of file
+runWebNNConformanceTests('cast', buildCast);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/clamp.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/clamp.https.any.js
index 7b60c41f2c..ab47ac9c5c 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/clamp.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/clamp.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API clamp operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-clamp
-testWebNNOperation('clamp', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('clamp', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/compute-arraybufferview-with-bigger-arraybuffer.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/compute-arraybufferview-with-bigger-arraybuffer.https.any.js
new file mode 100644
index 0000000000..62ce16c93e
--- /dev/null
+++ b/testing/web-platform/tests/webnn/conformance_tests/compute-arraybufferview-with-bigger-arraybuffer.https.any.js
@@ -0,0 +1,61 @@
+// META: title=test WebNN MLContext.compute() for ArrayBufferView created from bigger ArrayBuffer
+// META: global=window,dedicatedworker
+// META: variant=?gpu
+// META: script=../resources/utils.js
+
+'use strict';
+
+// These tests are used to reproduce the Chromium issue:
+// https://issues.chromium.org/issues/332151809
+
+if (navigator.ml) {
+ const variant = location.search.substring(1);
+ const contextOptions = kContextOptionsForVariant[variant];
+
+ let context;
+ let builder;
+
+ promise_setup(async () => {
+ let supported = false;
+ try {
+ context = await navigator.ml.createContext(contextOptions);
+ supported = true;
+ } catch (e) {
+ }
+ assert_implements(
+ supported, `Unable to create context for ${variant} variant`);
+ builder = new MLGraphBuilder(context);
+ });
+
+ promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const b = builder.relu(a);
+ const graph = await builder.build({b});
+ const arraybuffer = new ArrayBuffer(100);
+ const aBuffer =
+ new Float32Array(arraybuffer, /*byteOffset*/ 4, /*length*/ 2)
+ aBuffer.set([1, -1]);
+ const bBuffer = new Float32Array(2);
+ const results =
+ await context.compute(graph, {'a': aBuffer}, {'b': bBuffer});
+ assert_array_approx_equals_ulp(
+ results.outputs.b, [1, 0], /*nulp*/ 0, 'float32');
+ }, 'Test compute() working for input ArrayBufferView created from bigger ArrayBuffer');
+
+ promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const b = builder.relu(a);
+ const graph = await builder.build({b});
+ const aBuffer = new Float32Array(2);
+ aBuffer.set([1, -1]);
+ const arraybuffer = new ArrayBuffer(100);
+ const bBuffer =
+ new Float32Array(arraybuffer, /*byteOffset*/ 8, /*length*/ 2);
+ const results =
+ await context.compute(graph, {'a': aBuffer}, {'b': bBuffer});
+ assert_array_approx_equals_ulp(
+ results.outputs.b, [1, 0], /*nulp*/ 0, 'float32');
+ }, 'Test compute() working for output ArrayBufferView created from bigger ArrayBuffer');
+} else {
+ test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
+}
diff --git a/testing/web-platform/tests/webnn/conformance_tests/concat.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/concat.https.any.js
index 254e0b657b..619f20fe1c 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/concat.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/concat.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API concat operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-concat
-testWebNNOperation('concat', buildConcat); \ No newline at end of file
+runWebNNConformanceTests('concat', buildConcat);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/constant.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/constant.https.any.js
index 4814734886..79362947f1 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/constant.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/constant.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API constant
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-constant-range
-testWebNNOperation('constant', buildConstantRange); \ No newline at end of file
+runWebNNConformanceTests('constant', buildConstantRange);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/conv2d.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/conv2d.https.any.js
index 0d9a621356..34af583162 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/conv2d.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/conv2d.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API conv2d operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-conv2d
-testWebNNOperation('conv2d', buildConv2d); \ No newline at end of file
+runWebNNConformanceTests('conv2d', buildConv2d);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/conv_transpose2d.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/conv_transpose2d.https.any.js
index ee5d28c72a..2943e67851 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/conv_transpose2d.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/conv_transpose2d.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API convTranspose2d operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-convtranspose2d
-testWebNNOperation('convTranspose2d', buildConvTranspose2d); \ No newline at end of file
+runWebNNConformanceTests('convTranspose2d', buildConvTranspose2d);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/elementwise_binary.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/elementwise_binary.https.any.js
index 5db14a43a1..a85a06e1d2 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/elementwise_binary.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/elementwise_binary.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API element-wise binary operations
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,6 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-binary
-testWebNNOperation(['add', 'sub', 'mul', 'div', 'max', 'min', 'pow'], buildOperationWithTwoInputs); \ No newline at end of file
+runWebNNConformanceTests(
+ ['add', 'sub', 'mul', 'div', 'max', 'min', 'pow'],
+ buildOperationWithTwoInputs);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/elementwise_logical.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/elementwise_logical.https.any.js
index a60c199447..3d3a825f9c 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/elementwise_logical.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/elementwise_logical.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API element-wise logical operations
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,14 +9,17 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-logical
-testWebNNOperation(
- [
- 'equal',
- 'greater',
- 'greaterOrEqual',
- 'lesser',
- 'lesserOrEqual',
- ],
- buildOperationWithTwoInputs
-);
-testWebNNOperation('logicalNot', buildOperationWithSingleInput); \ No newline at end of file
+if (navigator.ml) {
+ testWebNNOperation(
+ [
+ 'equal',
+ 'greater',
+ 'greaterOrEqual',
+ 'lesser',
+ 'lesserOrEqual',
+ ],
+ buildOperationWithTwoInputs);
+ testWebNNOperation('logicalNot', buildOperationWithSingleInput);
+} else {
+ test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
+}
diff --git a/testing/web-platform/tests/webnn/conformance_tests/elementwise_unary.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/elementwise_unary.https.any.js
index 8029539eda..f202af01e5 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/elementwise_unary.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/elementwise_unary.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API element-wise unary operations
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,7 +9,9 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-unary
-testWebNNOperation(
- ['abs', 'ceil', 'cos', 'erf', 'exp', 'floor', 'identity', 'log', 'neg', 'reciprocal', 'sin', 'sqrt', 'tan'],
- buildOperationWithSingleInput
-); \ No newline at end of file
+runWebNNConformanceTests(
+ [
+ 'abs', 'ceil', 'cos', 'erf', 'exp', 'floor', 'identity', 'log', 'neg',
+ 'reciprocal', 'sin', 'sqrt', 'tan'
+ ],
+ buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/elu.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/elu.https.any.js
index 382faa97fd..ac1c19a80b 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/elu.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/elu.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API elu operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-elu
-testWebNNOperation('elu', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('elu', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/expand.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/expand.https.any.js
index b1be129eac..e7bf106f96 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/expand.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/expand.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API expand operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,5 +9,5 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-expand
-// reuse buildReshape method
-testWebNNOperation('expand', buildReshape); \ No newline at end of file
+// Reuse buildReshape method
+runWebNNConformanceTests('expand', buildReshape);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gather.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gather.https.any.js
index 39b1970563..504f2dd792 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/gather.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/gather.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API gather operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-gather
-testWebNNOperation('gather', buildOperationWithTwoInputs); \ No newline at end of file
+runWebNNConformanceTests('gather', buildOperationWithTwoInputs);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gemm.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gemm.https.any.js
index 61fd7c9b39..03a836a44a 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/gemm.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/gemm.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API gemm operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-gemm
-testWebNNOperation('gemm', buildGemm); \ No newline at end of file
+runWebNNConformanceTests('gemm', buildGemm);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/arg_min_max.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/arg_min_max.https.any.js
deleted file mode 100644
index c700ee5cad..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/arg_min_max.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API argMin/Max operations
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-argminmax
-
-testWebNNOperation(['argMin', 'argMax'], buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/batch_normalization.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/batch_normalization.https.any.js
deleted file mode 100644
index 534cdf6365..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/batch_normalization.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API batchNormalization operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-batchnorm
-
-testWebNNOperation('batchNormalization', buildBatchNorm, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/buffer.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/buffer.https.any.js
deleted file mode 100644
index 225bc40185..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/buffer.https.any.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// META: title=test WebNN API buffer operations
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlbuffer
-
-testCreateWebNNBuffer("create", 4, 'gpu');
-
-testDestroyWebNNBuffer('destroyTwice', 'gpu');
-
-testReadWebNNBuffer('read', 'gpu');
-
-testWriteWebNNBuffer('write', 'gpu');
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/cast.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/cast.https.any.js
deleted file mode 100644
index e4309ffd8e..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/cast.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API cast operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-cast
-
-testWebNNOperation('cast', buildCast, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/clamp.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/clamp.https.any.js
deleted file mode 100644
index 9b3f93ecc7..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/clamp.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API clamp operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-clamp
-
-testWebNNOperation('clamp', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/concat.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/concat.https.any.js
deleted file mode 100644
index c0cfb8626b..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/concat.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API concat operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-concat
-
-testWebNNOperation('concat', buildConcat, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/constant.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/constant.https.any.js
deleted file mode 100644
index 77b4d889a2..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/constant.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API constant
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-constant-range
-
-testWebNNOperation('constant', buildConstantRange, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/conv2d.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/conv2d.https.any.js
deleted file mode 100644
index 770540abd8..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/conv2d.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API conv2d operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-conv2d
-
-testWebNNOperation('conv2d', buildConv2d, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/conv_transpose2d.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/conv_transpose2d.https.any.js
deleted file mode 100644
index 08c441b5b4..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/conv_transpose2d.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API convTranspose2d operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-convtranspose2d
-
-testWebNNOperation('convTranspose2d', buildConvTranspose2d, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_binary.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_binary.https.any.js
deleted file mode 100644
index 8b9fa486f8..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_binary.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API element-wise binary operations
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-binary
-
-testWebNNOperation(['add', 'sub', 'mul', 'div', 'max', 'min', 'pow'], buildOperationWithTwoInputs, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_logical.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_logical.https.any.js
deleted file mode 100644
index 70a887a147..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_logical.https.any.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// META: title=test WebNN API element-wise logical operations
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-logical
-
-testWebNNOperation(
- [
- 'equal',
- 'greater',
- 'greaterOrEqual',
- 'lesser',
- 'lesserOrEqual',
- ],
- buildOperationWithTwoInputs, 'gpu'
-);
-testWebNNOperation('logicalNot', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_unary.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_unary.https.any.js
deleted file mode 100644
index 8871129311..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/elementwise_unary.https.any.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// META: title=test WebNN API element-wise unary operations
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-unary
-
-testWebNNOperation(
- ['abs', 'ceil', 'cos', 'erf', 'exp', 'floor', 'identity', 'log', 'neg', 'reciprocal', 'sin', 'sqrt', 'tan'],
- buildOperationWithSingleInput, 'gpu'
-); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/elu.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/elu.https.any.js
deleted file mode 100644
index db14442641..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/elu.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API elu operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-elu
-
-testWebNNOperation('elu', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/expand.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/expand.https.any.js
deleted file mode 100644
index f46f463781..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/expand.https.any.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// META: title=test WebNN API expand operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-expand
-
-// reuse buildReshape method
-testWebNNOperation('expand', buildReshape, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/gather.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/gather.https.any.js
deleted file mode 100644
index 8e457192d8..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/gather.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API gather operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-gather
-
-testWebNNOperation('gather', buildOperationWithTwoInputs, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/gemm.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/gemm.https.any.js
deleted file mode 100644
index f288c31bed..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/gemm.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API gemm operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-gemm
-
-testWebNNOperation('gemm', buildGemm, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/hard_sigmoid.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/hard_sigmoid.https.any.js
deleted file mode 100644
index d40e42a211..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/hard_sigmoid.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API hardSigmoid operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-hard-sigmoid
-
-testWebNNOperation('hardSigmoid', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/hard_swish.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/hard_swish.https.any.js
deleted file mode 100644
index 031e65ee16..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/hard_swish.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API tanh operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-hard-swish
-
-testWebNNOperation('hardSwish', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/instance_normalization.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/instance_normalization.https.any.js
deleted file mode 100644
index ecfaac71ee..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/instance_normalization.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API instanceNormalization operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-instancenorm
-
-testWebNNOperation('instanceNormalization', buildLayerNorm, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/layer_normalization.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/layer_normalization.https.any.js
deleted file mode 100644
index 0e4f6caebf..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/layer_normalization.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API layerNormalization operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-layernorm
-
-testWebNNOperation('layerNormalization', buildLayerNorm, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/leaky_relu.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/leaky_relu.https.any.js
deleted file mode 100644
index 9fab2353b9..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/leaky_relu.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API leakyRelu operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-leakyrelu
-
-testWebNNOperation('leakyRelu', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/linear.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/linear.https.any.js
deleted file mode 100644
index ccec2c3eac..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/linear.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API linear operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-linear
-
-testWebNNOperation('linear', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/matmul.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/matmul.https.any.js
deleted file mode 100644
index 635ce84ac6..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/matmul.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API matmul operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-matmul
-
-testWebNNOperation('matmul', buildOperationWithTwoInputs, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/pad.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/pad.https.any.js
deleted file mode 100644
index f313e2c9f9..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/pad.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API pad operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-pad
-
-testWebNNOperation('pad', buildPad, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/pooling.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/pooling.https.any.js
deleted file mode 100644
index 837bca2c71..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/pooling.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API pooling operations
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-pool2d
-
-testWebNNOperation(['averagePool2d', 'l2Pool2d', 'maxPool2d'], buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/prelu.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/prelu.https.any.js
deleted file mode 100644
index 475cd9e5ce..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/prelu.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API prelu operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-prelu
-
-testWebNNOperation('prelu', buildOperationWithTwoInputs, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/reduction.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/reduction.https.any.js
deleted file mode 100644
index 0f3cefa02e..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/reduction.https.any.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// META: title=test WebNN API reduction operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-reduce
-
-testWebNNOperation(
- [
- 'reduceL1',
- 'reduceL2',
- 'reduceLogSum',
- 'reduceLogSumExp',
- 'reduceMax',
- 'reduceMean',
- 'reduceMin',
- 'reduceProduct',
- 'reduceSum',
- 'reduceSumSquare',
- ],
- buildOperationWithSingleInput, 'gpu'
-); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/relu.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/relu.https.any.js
deleted file mode 100644
index d1a35367df..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/relu.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API relu operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-relu
-
-testWebNNOperation('relu', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/resample2d.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/resample2d.https.any.js
deleted file mode 100644
index dd8e441946..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/resample2d.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API resample2d operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-resample2d-method
-
-testWebNNOperation('resample2d', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/reshape.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/reshape.https.any.js
deleted file mode 100644
index b0217d2e67..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/reshape.https.any.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// META: title=test WebNN API reshape operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-reshape
-
-testWebNNOperation('reshape', buildReshape, 'gpu');
-
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/sigmoid.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/sigmoid.https.any.js
deleted file mode 100644
index 26116c0ff9..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/sigmoid.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API sigmoid operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-sigmoid
-
-testWebNNOperation('sigmoid', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/slice.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/slice.https.any.js
deleted file mode 100644
index 1710c79a9c..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/slice.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API slice operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-slice
-
-testWebNNOperation('slice', buildSlice, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/softmax.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/softmax.https.any.js
deleted file mode 100644
index 9eaffe2beb..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/softmax.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API softmax operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-softmax
-
-testWebNNOperation('softmax', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/softplus.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/softplus.https.any.js
deleted file mode 100644
index 5f06846113..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/softplus.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API softplus operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-softplus
-
-testWebNNOperation('softplus', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/softsign.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/softsign.https.any.js
deleted file mode 100644
index eac0b7ec40..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/softsign.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API softsign operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-softsign
-
-testWebNNOperation('softsign', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/split.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/split.https.any.js
deleted file mode 100644
index 3b0aafd787..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/split.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API split operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-split
-
-testWebNNOperation('split', buildSplit, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/tanh.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/tanh.https.any.js
deleted file mode 100644
index 3029f4865a..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/tanh.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API tanh operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-tanh
-
-testWebNNOperation('tanh', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/transpose.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/transpose.https.any.js
deleted file mode 100644
index 295ef43ec1..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/transpose.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API transpose operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-transpose
-
-testWebNNOperation('transpose', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/triangular.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/triangular.https.any.js
deleted file mode 100644
index 3e1b0d5ab1..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/triangular.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API triangular operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-triangular
-
-testWebNNOperation('triangular', buildOperationWithSingleInput, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/where.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/where.https.any.js
deleted file mode 100644
index 49c6cbd4e3..0000000000
--- a/testing/web-platform/tests/webnn/conformance_tests/gpu/where.https.any.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// META: title=test WebNN API where operation
-// META: global=window,dedicatedworker
-// META: script=../../resources/utils.js
-// META: timeout=long
-
-'use strict';
-
-// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-where
-
-testWebNNOperation('where', buildWhere, 'gpu'); \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/conformance_tests/hard_sigmoid.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/hard_sigmoid.https.any.js
index 8161a24538..55391e7f1c 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/hard_sigmoid.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/hard_sigmoid.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API hardSigmoid operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-hard-sigmoid
-testWebNNOperation('hardSigmoid', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('hardSigmoid', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/hard_swish.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/hard_swish.https.any.js
index b4a7c53d8d..24b8c413bb 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/hard_swish.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/hard_swish.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API tanh operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-hard-swish
-testWebNNOperation('hardSwish', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('hardSwish', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/instance_normalization.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/instance_normalization.https.any.js
index fce879172e..fc339e5bab 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/instance_normalization.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/instance_normalization.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API instanceNormalization operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-instancenorm
-testWebNNOperation('instanceNormalization', buildLayerNorm); \ No newline at end of file
+runWebNNConformanceTests('instanceNormalization', buildLayerNorm);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/layer_normalization.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/layer_normalization.https.any.js
index ab8a50cc03..ea3cd04240 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/layer_normalization.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/layer_normalization.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API layerNormalization operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-layernorm
-testWebNNOperation('layerNormalization', buildLayerNorm); \ No newline at end of file
+runWebNNConformanceTests('layerNormalization', buildLayerNorm);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/leaky_relu.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/leaky_relu.https.any.js
index 2b6f17e95d..b2a4055bde 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/leaky_relu.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/leaky_relu.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API leakyRelu operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-leakyrelu
-testWebNNOperation('leakyRelu', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('leakyRelu', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/linear.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/linear.https.any.js
index 465b697f29..0e22f7a036 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/linear.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/linear.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API linear operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-linear
-testWebNNOperation('linear', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('linear', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/matmul.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/matmul.https.any.js
index 64eeb37f08..da78230579 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/matmul.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/matmul.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API matmul operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-matmul
-testWebNNOperation('matmul', buildOperationWithTwoInputs); \ No newline at end of file
+runWebNNConformanceTests('matmul', buildOperationWithTwoInputs);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/pad.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/pad.https.any.js
index f1a2400d1c..d733cbb6ed 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/pad.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/pad.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API pad operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-pad
-testWebNNOperation('pad', buildPad); \ No newline at end of file
+runWebNNConformanceTests('pad', buildPad);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/parallel-compute.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/parallel-compute.https.any.js
new file mode 100644
index 0000000000..642fec9f73
--- /dev/null
+++ b/testing/web-platform/tests/webnn/conformance_tests/parallel-compute.https.any.js
@@ -0,0 +1,19 @@
+// META: title=test parallel WebNN API compute operations
+// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
+// META: script=../resources/utils.js
+// META: timeout=long
+
+'use strict';
+
+// https://webmachinelearning.github.io/webnn/#api-mlcontext-compute
+
+if (navigator.ml) {
+ testParallelCompute();
+} else {
+ // Show indication to users why the test failed
+ test(
+ () => assert_not_equals(
+ navigator.ml, undefined, 'ml property is defined on navigator'));
+}
diff --git a/testing/web-platform/tests/webnn/conformance_tests/pooling.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/pooling.https.any.js
index 400d5ed37d..de2ae35a9c 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/pooling.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/pooling.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API pooling operations
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,5 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-pool2d
-testWebNNOperation(['averagePool2d', 'l2Pool2d', 'maxPool2d'], buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests(
+ ['averagePool2d', 'l2Pool2d', 'maxPool2d'], buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/prelu.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/prelu.https.any.js
index 83cc9db4b4..9337211e54 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/prelu.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/prelu.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API prelu operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-prelu
-testWebNNOperation('prelu', buildOperationWithTwoInputs); \ No newline at end of file
+runWebNNConformanceTests('prelu', buildOperationWithTwoInputs);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/reduction.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/reduction.https.any.js
index 30bfb4ba7a..c291906ba1 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/reduction.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/reduction.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API reduction operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,18 +9,17 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-reduce
-testWebNNOperation(
- [
- 'reduceL1',
- 'reduceL2',
- 'reduceLogSum',
- 'reduceLogSumExp',
- 'reduceMax',
- 'reduceMean',
- 'reduceMin',
- 'reduceProduct',
- 'reduceSum',
- 'reduceSumSquare',
- ],
- buildOperationWithSingleInput
-); \ No newline at end of file
+runWebNNConformanceTests(
+ [
+ 'reduceL1',
+ 'reduceL2',
+ 'reduceLogSum',
+ 'reduceLogSumExp',
+ 'reduceMax',
+ 'reduceMean',
+ 'reduceMin',
+ 'reduceProduct',
+ 'reduceSum',
+ 'reduceSumSquare',
+ ],
+ buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/relu.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/relu.https.any.js
index 51e427898f..7cb23eea1b 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/relu.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/relu.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API relu operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-relu
-testWebNNOperation('relu', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('relu', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/resample2d.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/resample2d.https.any.js
index 0b5b3e0032..b5bdda7197 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/resample2d.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/resample2d.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API resample2d operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-resample2d-method
-testWebNNOperation('resample2d', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('resample2d', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/reshape.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/reshape.https.any.js
index c0dafb176c..a7d03b2a0c 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/reshape.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/reshape.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API reshape operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,5 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-reshape
-testWebNNOperation('reshape', buildReshape);
-
+runWebNNConformanceTests('reshape', buildReshape);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/sigmoid.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/sigmoid.https.any.js
index 186f468918..9730b548b5 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/sigmoid.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/sigmoid.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API sigmoid operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-sigmoid
-testWebNNOperation('sigmoid', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('sigmoid', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/slice.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/slice.https.any.js
index 6441204517..b316ea58c4 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/slice.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/slice.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API slice operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-slice
-testWebNNOperation('slice', buildSlice); \ No newline at end of file
+runWebNNConformanceTests('slice', buildSlice);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/softmax.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/softmax.https.any.js
index 143b7d969f..a68a32c45f 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/softmax.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/softmax.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API softmax operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-softmax
-testWebNNOperation('softmax', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('softmax', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/softplus.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/softplus.https.any.js
index fcd6410bdb..7d89b117eb 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/softplus.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/softplus.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API softplus operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-softplus
-testWebNNOperation('softplus', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('softplus', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/softsign.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/softsign.https.any.js
index 6e26afdade..e175e0de56 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/softsign.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/softsign.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API softsign operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-softsign
-testWebNNOperation('softsign', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('softsign', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/split.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/split.https.any.js
index 0de6cb4d8d..78d707687f 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/split.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/split.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API split operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-split
-testWebNNOperation('split', buildSplit); \ No newline at end of file
+runWebNNConformanceTests('split', buildSplit);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/tanh.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/tanh.https.any.js
index c5d1f86ab1..e3ab5e9192 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/tanh.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/tanh.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API tanh operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-tanh
-testWebNNOperation('tanh', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('tanh', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/transpose.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/transpose.https.any.js
index 746e53d512..83bd7a45c1 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/transpose.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/transpose.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API transpose operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-transpose
-testWebNNOperation('transpose', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('transpose', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/triangular.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/triangular.https.any.js
index 503f310620..499f60ed36 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/triangular.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/triangular.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API triangular operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-triangular
-testWebNNOperation('triangular', buildOperationWithSingleInput); \ No newline at end of file
+runWebNNConformanceTests('triangular', buildOperationWithSingleInput);
diff --git a/testing/web-platform/tests/webnn/conformance_tests/where.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/where.https.any.js
index 7926221d3a..4ab453ab24 100644
--- a/testing/web-platform/tests/webnn/conformance_tests/where.https.any.js
+++ b/testing/web-platform/tests/webnn/conformance_tests/where.https.any.js
@@ -1,5 +1,7 @@
// META: title=test WebNN API where operation
// META: global=window,dedicatedworker
+// META: variant=?cpu
+// META: variant=?gpu
// META: script=../resources/utils.js
// META: timeout=long
@@ -7,4 +9,4 @@
// https://webmachinelearning.github.io/webnn/#api-mlgraphbuilder-where
-testWebNNOperation('where', buildWhere); \ No newline at end of file
+runWebNNConformanceTests('where', buildWhere);
diff --git a/testing/web-platform/tests/webnn/resources/test_data/arg_max.json b/testing/web-platform/tests/webnn/resources/test_data/arg_max.json
index d2fe9e62ca..348a54dc24 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/arg_max.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/arg_max.json
@@ -460,7 +460,7 @@
}
},
"options": {
- "keepDimensions": true
+ "keepDimensions": false
},
"expected": {
"name": "output",
diff --git a/testing/web-platform/tests/webnn/resources/test_data/arg_min.json b/testing/web-platform/tests/webnn/resources/test_data/arg_min.json
index 132a2dc3e8..330afbc710 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/arg_min.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/arg_min.json
@@ -460,7 +460,7 @@
}
},
"options": {
- "keepDimensions": true
+ "keepDimensions": false
},
"expected": {
"name": "output",
diff --git a/testing/web-platform/tests/webnn/resources/test_data/matmul.json b/testing/web-platform/tests/webnn/resources/test_data/matmul.json
index f3a03e442e..cc1789ee25 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/matmul.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/matmul.json
@@ -1,332 +1,6 @@
{
"tests": [
{
- "name": "matmul float32 constant 1D and 1D tensors all positive produces a scalar",
- "inputs": {
- "a": {
- "shape": [4],
- "data": [
- 50.10142534731317,
- 22.2193058046253,
- 34.65448469299386,
- 36.35148095671881
- ],
- "type": "float32",
- "constant": true
- },
- "b": {
- "shape": [4],
- "data": [
- 27.829805134194842,
- 83.14548502311283,
- 34.4128942110155,
- 83.20379675185079
- ],
- "type": "float32",
- "constant": true
- }
- },
- "expected": {
- "name": "output",
- "data": 7458.89013671875,
- "type": "float32"
- }
- },
- {
- "name": "matmul float32 1D and 1D tensors all positive produces a scalar",
- "inputs": {
- "a": {
- "shape": [4],
- "data": [
- 50.10142534731317,
- 22.2193058046253,
- 34.65448469299386,
- 36.35148095671881
- ],
- "type": "float32"
- },
- "b": {
- "shape": [4],
- "data": [
- 27.829805134194842,
- 83.14548502311283,
- 34.4128942110155,
- 83.20379675185079
- ],
- "type": "float32"
- }
- },
- "expected": {
- "name": "output",
- "data": 7458.89013671875,
- "type": "float32"
- }
- },
- {
- "name": "matmul float32 1D and 1D tensors all negative produces a scalar",
- "inputs": {
- "a": {
- "shape": [4],
- "data": [
- -86.60583871803968,
- -94.74202421330796,
- -86.16720380573273,
- -76.0215630990851
- ],
- "type": "float32"
- },
- "b": {
- "shape": [4],
- "data": [
- -21.22328469397374,
- -56.66441199254357,
- -77.66753889428908,
- -56.55797454862353
- ],
- "type": "float32"
- }
- },
- "expected": {
- "name": "output",
- "data": 18198.58203125,
- "type": "float32"
- }
- },
- {
- "name": "matmul float32 positive 1D and negative 1D tensors produces a scalar",
- "inputs": {
- "a": {
- "shape": [4],
- "data": [
- 50.10142534731317,
- 22.2193058046253,
- 34.65448469299386,
- 36.35148095671881
- ],
- "type": "float32"
- },
- "b": {
- "shape": [4],
- "data": [
- -21.22328469397374,
- -56.66441199254357,
- -77.66753889428908,
- -56.55797454862353
- ],
- "type": "float32"
- }
- },
- "expected": {
- "name": "output",
- "data": -7069.85546875,
- "type": "float32"
- }
- },
- {
- "name": "matmul float32 negative 1D and positive 1D tensors produces a scalar",
- "inputs": {
- "a": {
- "shape": [4],
- "data": [
- -86.60583871803968,
- -94.74202421330796,
- -86.16720380573273,
- -76.0215630990851
- ],
- "type": "float32"
- },
- "b": {
- "shape": [4],
- "data": [
- 27.829805134194842,
- 83.14548502311283,
- 34.4128942110155,
- 83.20379675185079
- ],
- "type": "float32"
- }
- },
- "expected": {
- "name": "output",
- "data": -19578.140625,
- "type": "float32"
- }
- },
- {
- "name": "matmul float32 1D and 2D tensors",
- "inputs": {
- "a": {
- "shape": [4],
- "data": [
- 50.10142534731317,
- 22.2193058046253,
- 34.65448469299386,
- 36.35148095671881
- ],
- "type": "float32"
- },
- "b": {
- "shape": [4, 5],
- "data": [
- 88.17004408170853,
- 78.40126706348094,
- 14.819002753540623,
- 3.692303791736573,
- 45.9064286713635,
- 43.08391896733015,
- 47.19946845924572,
- 60.925216107016425,
- 8.162760351602216,
- 20.33326305093228,
- 20.438397895943282,
- 27.01940859922867,
- 15.601424432184263,
- 87.46969388883927,
- 65.79554455585657,
- 69.31696864490797,
- 31.984439910782992,
- 12.291812891860388,
- 13.304834654547172,
- 85.26705387930089
- ],
- "type": "float32"
- }
- },
- "expected": {
- "name": "output",
- "shape": [1, 5],
- "data": [
- 8602.796875,
- 7075.7802734375,
- 3083.654052734375,
- 3881.228271484375,
- 8131.462890625
- ],
- "type": "float32"
- }
- },
- {
- "name": "matmul float32 1D and 4D tensors",
- "inputs": {
- "a": {
- "shape": [4],
- "data": [
- 50.10142534731317,
- 22.2193058046253,
- 34.65448469299386,
- 36.35148095671881
- ],
- "type": "float32"
- },
- "b": {
- "shape": [2, 2, 4, 2],
- "data": [
- 71.54684436671175,
- 78.61926127874348,
- 2.246814764541316,
- 12.044773359659434,
- 1.8342867333124069,
- 82.09732511549477,
- 43.884761946067094,
- 5.616231825100204,
- 34.67424097413332,
- 49.152710076333506,
- 75.34904358690912,
- 84.74523302920429,
- 36.56497325521975,
- 22.89479672718755,
- 15.02636975800511,
- 66.49530785246675,
- 65.81345056776044,
- 26.749681209347376,
- 19.415639234175774,
- 98.60692665127114,
- 65.39448996549784,
- 56.47023672202065,
- 80.64523905250766,
- 82.20401464839868,
- 70.84606482516416,
- 50.27994995977012,
- 67.39406108056262,
- 75.35806805146241,
- 2.7487208784167327,
- 68.0645872775828,
- 70.73791158057968,
- 46.26436742398676
- ],
- "type": "float32"
- }
- },
- "expected": {
- "name": "output",
- "shape": [2, 2, 1, 2],
- "data": [
- 5293.36376953125,
- 7255.7626953125,
- 5224.80322265625,
- 7556.21142578125,
- 8926.5361328125,
- 8476.359375,
- 7713.62158203125,
- 8234.0224609375
- ],
- "type": "float32"
- }
- },
- {
- "name": "matmul float32 2D and 1D tensors",
- "inputs": {
- "a": {
- "shape": [5, 4],
- "data": [
- 88.17004408170853,
- 43.08391896733015,
- 20.438397895943282,
- 69.31696864490797,
- 78.40126706348094,
- 47.19946845924572,
- 27.01940859922867,
- 31.984439910782992,
- 14.819002753540623,
- 60.925216107016425,
- 15.601424432184263,
- 12.291812891860388,
- 3.692303791736573,
- 8.162760351602216,
- 87.46969388883927,
- 13.304834654547172,
- 45.9064286713635,
- 20.33326305093228,
- 65.79554455585657,
- 85.26705387930089
- ],
- "type": "float32"
- },
- "b": {
- "shape": [4],
- "data": [
- 50.10142534731317,
- 22.2193058046253,
- 34.65448469299386,
- 36.35148095671881
- ],
- "type": "float32"
- }
- },
- "expected": {
- "name": "output",
- "shape": [5, 1],
- "data": [
- 8602.796875,
- 7075.7802734375,
- 3083.654052734375,
- 3881.228271484375,
- 8131.462890625
- ],
- "type": "float32"
- }
- },
- {
"name": "matmul float32 2D and 2D tensors",
"inputs": {
"a": {
@@ -750,64 +424,6 @@
}
},
{
- "name": "matmul float32 3D and 1D tensors",
- "inputs": {
- "a": {
- "shape": [2, 3, 4],
- "data": [
- 56.46701250066562,
- 99.86045478237251,
- 71.05493372292567,
- 32.45438455331333,
- 17.310747999630017,
- 2.586275053048559,
- 92.31499166302054,
- 96.9758519231732,
- 26.4721315276526,
- 77.67031776320978,
- 29.278788710989147,
- 82.12142428847062,
- 89.89308471484885,
- 82.49795321217854,
- 64.36866008901963,
- 23.75928513568486,
- 6.67026681065197,
- 81.55583129445503,
- 16.142963270263433,
- 57.45134849716054,
- 26.82641739603182,
- 85.0296980735713,
- 36.198863464757956,
- 89.60960360138286
- ],
- "type": "float32"
- },
- "b": {
- "shape": [4],
- "data": [
- 27.829805134194842,
- 83.14548502311283,
- 34.4128942110155,
- 83.20379675185079
- ],
- "type": "float32"
- }
- },
- "expected": {
- "name": "output",
- "shape": [2, 3, 1],
- "data": [
- 15019.9462890625,
- 11942.376953125,
- 15035.0322265625,
- 13553.013671875,
- 12302.328125,
- 16517.9765625
- ],
- "type": "float32"
- }
- },
- {
"name": "matmul float32 4D and 4D (broadcast) tensors",
"inputs": {
"a": {
diff --git a/testing/web-platform/tests/webnn/resources/test_data/prelu.json b/testing/web-platform/tests/webnn/resources/test_data/prelu.json
index cf79bee7a9..14a7c412dd 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/prelu.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/prelu.json
@@ -1,6 +1,34 @@
{
"tests": [
{
+ "name": "prelu float32 0D scalar",
+ "inputs": {
+ "x": {
+ "shape": [],
+ "data": [
+ -4.794857500523286
+ ],
+ "type": "float32"
+ },
+ "slope": {
+ "shape": [],
+ "data": [
+ 1.1202747481570352
+ ],
+ "type": "float32",
+ "constant": true
+ }
+ },
+ "expected": {
+ "name": "output",
+ "shape": [],
+ "data": [
+ -5.371557712554932
+ ],
+ "type": "float32"
+ }
+ },
+ {
"name": "prelu float32 1D constant tensors",
"inputs": {
"x": {
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_l1.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_l1.json
index 7cbc442511..c3f2b618e9 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_l1.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_l1.json
@@ -676,7 +676,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"name": "output",
@@ -725,11 +726,12 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
"name": "output",
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
108.43173217773438,
315.6007995605469,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_l2.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_l2.json
index 7e59a45d5e..d83eea9dfb 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_l2.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_l2.json
@@ -676,7 +676,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"name": "output",
@@ -725,11 +726,12 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
"name": "output",
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
138.580078125,
166.67791748046875,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum.json
index 250398d227..061e12ad51 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum.json
@@ -596,7 +596,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"name": "output",
@@ -645,11 +646,12 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
"name": "output",
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
5.7273993492126465,
5.64375114440918,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum_exp.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum_exp.json
index b7f39abd52..3577d6aa9e 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum_exp.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_log_sum_exp.json
@@ -676,7 +676,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"name": "output",
@@ -725,11 +726,12 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
"name": "output",
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
8.563796997070312,
5.500619411468506,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_max.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_max.json
index 967aea8bf4..11ed0ed919 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_max.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_max.json
@@ -556,7 +556,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"name": "output",
@@ -605,11 +606,12 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
"name": "output",
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
90.42288208007812,
94.99645233154297,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_mean.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_mean.json
index 5a48952c06..8c26d0a562 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_mean.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_mean.json
@@ -670,7 +670,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"shape": [2, 2],
@@ -718,10 +719,11 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
52.287559509277344,
45.10261917114258,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_min.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_min.json
index 92de75e92a..6c26df5db1 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_min.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_min.json
@@ -556,7 +556,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"name": "output",
@@ -605,11 +606,12 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
"name": "output",
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
-87.9623031616211,
-53.747413635253906,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_product.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_product.json
index 691bf4da9b..d58af30ec1 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_product.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_product.json
@@ -556,7 +556,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"name": "output",
@@ -605,11 +606,12 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
"name": "output",
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
-3638925568,
6523364352,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_sum.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_sum.json
index df47a1a2b2..7027d38b67 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_sum.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_sum.json
@@ -670,7 +670,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"shape": [2, 2],
@@ -718,10 +719,11 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
355.21942138671875,
185.98255920410156,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/reduce_sum_square.json b/testing/web-platform/tests/webnn/resources/test_data/reduce_sum_square.json
index 8ac373e4b3..bd2ebb341a 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/reduce_sum_square.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/reduce_sum_square.json
@@ -676,7 +676,8 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": false
},
"expected": {
"name": "output",
@@ -725,11 +726,12 @@
}
},
"options": {
- "axes": [1, 3]
+ "axes": [1, 3],
+ "keepDimensions": true
},
"expected": {
"name": "output",
- "shape": [1, 2, 2, 1],
+ "shape": [2, 1, 2, 1],
"data": [
12302.474609375,
22772.77734375,
diff --git a/testing/web-platform/tests/webnn/resources/test_data/softplus.json b/testing/web-platform/tests/webnn/resources/test_data/softplus.json
index eb05b7b281..373612d5ca 100644
--- a/testing/web-platform/tests/webnn/resources/test_data/softplus.json
+++ b/testing/web-platform/tests/webnn/resources/test_data/softplus.json
@@ -1,7 +1,7 @@
-{ // softplus: The calculation follows the expression ln(1 + exp(steepness * x)) / steepness.
+{ // softplus: The calculation follows the expression ln(1 + exp(x)).
"tests": [
{
- "name": "softplus float32 1D constant tensor default options", // default options: {steepness: 1}
+ "name": "softplus float32 1D constant tensor",
"inputs": {
"x": {
"shape": [24],
@@ -68,7 +68,7 @@
}
},
{
- "name": "softplus float32 1D tensor default options", // default options: {steepness: 1}
+ "name": "softplus float32 1D tensor",
"inputs": {
"x": {
"shape": [24],
@@ -134,7 +134,7 @@
}
},
{
- "name": "softplus float32 2D tensor default options",
+ "name": "softplus float32 2D tensor",
"inputs": {
"x": {
"shape": [4, 6],
@@ -200,7 +200,7 @@
}
},
{
- "name": "softplus float32 3D tensor default options",
+ "name": "softplus float32 3D tensor",
"inputs": {
"x": {
"shape": [2, 3, 4],
@@ -266,7 +266,7 @@
}
},
{
- "name": "softplus float32 4D tensor default options",
+ "name": "softplus float32 4D tensor",
"inputs": {
"x": {
"shape": [1, 2, 3, 4],
@@ -332,7 +332,7 @@
}
},
{
- "name": "softplus float32 5D tensor default options",
+ "name": "softplus float32 5D tensor",
"inputs": {
"x": {
"shape": [1, 2, 1, 3, 4],
@@ -396,144 +396,6 @@
],
"type": "float32"
}
- },
- {
- "name": "softplus both positive float32 4D tensor and options.steepness",
- "inputs": {
- "x": {
- "shape": [1, 2, 3, 4],
- "data": [
- 5.626614582460632,
- 5.167487045486892,
- 4.0146356193402655,
- 9.48003299650489,
- 9.989938045769978,
- 7.0654412821434125,
- 2.132681001794825,
- 8.187151346059956,
- 5.169976220175496,
- 2.1044997879382077,
- 3.523329401138895,
- 4.136340646976668,
- 1.7418719794295656,
- 5.145224066290767,
- 5.015515309165462,
- 0.045903935074711466,
- 2.9570898924917377,
- 3.959244712098706,
- 5.517926978255181,
- 7.192322388417094,
- 8.76492480390928,
- 1.3734704039113388,
- 8.930669016709397,
- 8.660283210871246
- ],
- "type": "float32"
- }
- },
- "options": {
- "steepness": 1.5104469060897827
- },
- "expected": {
- "name": "output",
- "shape": [1, 2, 3, 4],
- "data": [
- 5.626749515533447,
- 5.167757034301758,
- 4.016173362731934,
- 9.480032920837402,
- 9.989937782287598,
- 7.065456390380859,
- 2.158585548400879,
- 8.187153816223145,
- 5.170245170593262,
- 2.1315081119537354,
- 3.526555061340332,
- 4.137620449066162,
- 1.7879058122634888,
- 5.145503044128418,
- 5.015854835510254,
- 0.4822517931461334,
- 2.964651584625244,
- 3.960916519165039,
- 5.518085956573486,
- 7.19233512878418,
- 8.764925956726074,
- 1.4518096446990967,
- 8.930669784545898,
- 8.660284042358398
- ],
- "type": "float32"
- }
- },
- {
- "name": "softplus both negative float32 4D tensor and options.steepness",
- "inputs": {
- "x": {
- "shape": [1, 2, 3, 4],
- "data": [
- -5.584833476104802,
- -8.188738740810354,
- -8.981280004134987,
- -1.7315531899284586,
- -0.7266543578958906,
- -0.0034800119290885334,
- -7.378389455552106,
- -8.907525953796949,
- -6.0483786568116304,
- -6.328561142365743,
- -2.6006513567654626,
- -5.02005264196455,
- -2.0647716093484414,
- -1.5499896740695434,
- -2.221591675966657,
- -1.1088025713211636,
- -2.7854626064634385,
- -2.105037489961294,
- -5.144277741727352,
- -5.081219916574497,
- -7.499426297617635,
- -2.4305558382286545,
- -8.390520024268328,
- -0.07117499202643174
- ],
- "type": "float32"
- }
- },
- "options": {
- "steepness": -1.2985155767552126
- },
- "expected": {
- "name": "output",
- "shape": [1, 2, 3, 4],
- "data": [
- -5.585379123687744,
- -8.188756942749023,
- -8.981287002563477,
- -1.8088372945785522,
- -0.9798305630683899,
- -0.5355416536331177,
- -7.378442287445068,
- -8.907533645629883,
- -6.048677444458008,
- -6.328769207000732,
- -2.626511573791504,
- -5.021188259124756,
- -2.1157851219177246,
- -1.6465802192687988,
- -2.2634570598602295,
- -1.2725814580917358,
- -2.805877923965454,
- -2.1535322666168213,
- -5.145244121551514,
- -5.082269191741943,
- -7.499471664428711,
- -2.4626762866973877,
- -8.390534400939941,
- -0.5702091455459595
- ],
- "type": "float32"
- }
}
]
} \ No newline at end of file
diff --git a/testing/web-platform/tests/webnn/resources/utils.js b/testing/web-platform/tests/webnn/resources/utils.js
index d1dc0675a7..e5b80ae9f7 100644
--- a/testing/web-platform/tests/webnn/resources/utils.js
+++ b/testing/web-platform/tests/webnn/resources/utils.js
@@ -13,6 +13,15 @@ const TypedArrayDict = {
int64: BigInt64Array,
};
+const kContextOptionsForVariant = {
+ cpu: {
+ deviceType: 'cpu',
+ },
+ gpu: {
+ deviceType: 'gpu',
+ }
+};
+
// The maximum index to validate for the output's expected value.
const kMaximumIndexToValidate = 1000;
@@ -867,17 +876,15 @@ const run = async (operationName, context, builder, resources, buildFunc) => {
checkResults(operationName, namedOutputOperands, result.outputs, resources);
};
+const variant = location.search.substring(1);
+const contextOptions = kContextOptionsForVariant[variant];
+
/**
* Run WebNN operation tests.
* @param {(String[]|String)} operationName - An operation name array or an operation name
* @param {Function} buildFunc - A build function for an operation
- * @param {String} deviceType - The execution device type for this test
*/
-const testWebNNOperation = (operationName, buildFunc, deviceType = 'cpu') => {
- test(() => assert_not_equals(navigator.ml, undefined, "ml property is defined on navigator"));
- if (navigator.ml === undefined) {
- return;
- }
+const testWebNNOperation = (operationName, buildFunc) => {
let operationNameArray;
if (typeof operationName === 'string') {
operationNameArray = [operationName];
@@ -890,7 +897,14 @@ const testWebNNOperation = (operationName, buildFunc, deviceType = 'cpu') => {
operationNameArray.forEach((subOperationName) => {
const tests = loadTests(subOperationName);
promise_setup(async () => {
- context = await navigator.ml.createContext({deviceType});
+ let supported = false;
+ try {
+ context = await navigator.ml.createContext(contextOptions);
+ supported = true;
+ } catch (e) {
+ }
+ assert_implements(
+ supported, `Unable to create context for ${variant} variant`);
builder = new MLGraphBuilder(context);
});
for (const subTest of tests) {
@@ -901,6 +915,65 @@ const testWebNNOperation = (operationName, buildFunc, deviceType = 'cpu') => {
});
};
+/**
+ * WebNN parallel compute operation test.
+ */
+const testParallelCompute = () => {
+ let ml_context;
+ let ml_graph;
+
+ promise_setup(async () => {
+ let supported = false;
+ try {
+ ml_context = await navigator.ml.createContext(contextOptions);
+ supported = true;
+ } catch (e) {
+ }
+ assert_implements(
+ supported, `Unable to create context for ${variant} variant`);
+ // Construct a simple graph: A = B * 2.
+ const builder = new MLGraphBuilder(ml_context);
+ const operandType = {dataType: 'float32', dimensions: [1]};
+ const input_operand = builder.input('input', operandType);
+ const const_operand = builder.constant(operandType, Float32Array.from([2]));
+ const output_operand = builder.mul(input_operand, const_operand);
+ ml_graph = await builder.build({'output': output_operand});
+ });
+
+ promise_test(async () => {
+ const test_inputs = [1, 2, 3, 4];
+
+ const actual_outputs = await Promise.all(test_inputs.map(async (input) => {
+ let inputs = {'input': Float32Array.from([input])};
+ let outputs = {'output': new Float32Array(1)};
+ ({inputs, outputs} = await ml_context.compute(ml_graph, inputs, outputs));
+ return outputs.output[0];
+ }));
+
+ const expected_outputs = [2, 4, 6, 8];
+ assert_array_equals(actual_outputs, expected_outputs);
+ });
+};
+
+/**
+ * Run WebNN conformance tests by specified operation.
+ * @param {(String[]|String)} operationName - An operation name array or an
+ * operation name
+ * @param {Function} buildFunc - A build function for an operation
+ */
+const runWebNNConformanceTests = (operationName, buildFunc) => {
+ // Link to https://github.com/web-platform-tests/wpt/pull/44883
+ // Check navigator.ml is defined before trying to run WebNN tests
+ if (navigator.ml) {
+ testWebNNOperation(operationName, buildFunc);
+ } else {
+ // Show indication to users why the test failed
+ test(
+ () => assert_not_equals(
+ navigator.ml, undefined, 'ml property is defined on navigator'));
+ }
+};
+
// ref: http://stackoverflow.com/questions/32633585/how-do-you-convert-to-half-floats-in-javascript
const toHalf = (value) => {
let floatView = new Float32Array(1);
@@ -970,13 +1043,19 @@ const createBuffer = (context, bufferSize) => {
/**
* WebNN destroy buffer twice test.
* @param {String} testName - The name of the test operation.
- * @param {String} deviceType - The execution device type for this test.
*/
-const testDestroyWebNNBuffer = (testName, deviceType = 'cpu') => {
+const testDestroyWebNNBuffer = (testName) => {
let context;
let buffer;
promise_setup(async () => {
- context = await navigator.ml.createContext({deviceType});
+ let supported = false;
+ try {
+ context = await navigator.ml.createContext(contextOptions);
+ supported = true;
+ } catch (e) {
+ }
+ assert_implements(
+ supported, `Unable to create context for ${variant} variant`);
buffer = createBuffer(context, 4);
});
promise_test(async () => {
@@ -993,12 +1072,19 @@ const testDestroyWebNNBuffer = (testName, deviceType = 'cpu') => {
* WebNN create buffer test.
* @param {String} testName - The name of the test operation.
* @param {Number} bufferSize - Size of the buffer to create, in bytes.
- * @param {String} deviceType - The execution device type for this test.
*/
-const testCreateWebNNBuffer = (testName, bufferSize, deviceType = 'cpu') => {
+const testCreateWebNNBuffer = (testName, bufferSize) => {
let context;
+
promise_setup(async () => {
- context = await navigator.ml.createContext({deviceType});
+ let supported = false;
+ try {
+ context = await navigator.ml.createContext(contextOptions);
+ supported = true;
+ } catch (e) {
+ }
+ assert_implements(
+ supported, `Unable to create context for ${variant} variant`);
});
promise_test(async () => {
createBuffer(context, bufferSize);
@@ -1021,12 +1107,18 @@ const assert_buffer_data_equals = async (ml_context, ml_buffer, expected) => {
/**
* WebNN write buffer operation test.
* @param {String} testName - The name of the test operation.
- * @param {String} deviceType - The execution device type for this test.
*/
-const testWriteWebNNBuffer = (testName, deviceType = 'cpu') => {
+const testWriteWebNNBuffer = (testName) => {
let ml_context;
promise_setup(async () => {
- ml_context = await navigator.ml.createContext({deviceType});
+ let supported = false;
+ try {
+ ml_context = await navigator.ml.createContext(contextOptions);
+ supported = true;
+ } catch (e) {
+ }
+ assert_implements(
+ supported, `Unable to create context for ${variant} variant`);
});
promise_test(async () => {
@@ -1117,7 +1209,7 @@ const testWriteWebNNBuffer = (testName, deviceType = 'cpu') => {
return;
}
- let another_ml_context = await navigator.ml.createContext({deviceType});
+ let another_ml_context = await navigator.ml.createContext(contextOptions);
let another_ml_buffer = createBuffer(another_ml_context, ml_buffer.size);
let input_data = new Uint8Array(ml_buffer.size).fill(0xAA);
@@ -1131,12 +1223,18 @@ const testWriteWebNNBuffer = (testName, deviceType = 'cpu') => {
/**
* WebNN read buffer operation test.
* @param {String} testName - The name of the test operation.
- * @param {String} deviceType - The execution device type for this test.
*/
-const testReadWebNNBuffer = (testName, deviceType = 'cpu') => {
+const testReadWebNNBuffer = (testName) => {
let ml_context;
promise_setup(async () => {
- ml_context = await navigator.ml.createContext({deviceType});
+ let supported = false;
+ try {
+ ml_context = await navigator.ml.createContext(contextOptions);
+ supported = true;
+ } catch (e) {
+ }
+ assert_implements(
+ supported, `Unable to create context for ${variant} variant`);
});
promise_test(async t => {
@@ -1277,7 +1375,7 @@ const testReadWebNNBuffer = (testName, deviceType = 'cpu') => {
return;
}
- let another_ml_context = await navigator.ml.createContext({deviceType});
+ let another_ml_context = await navigator.ml.createContext(contextOptions);
let another_ml_buffer = createBuffer(another_ml_context, ml_buffer.size);
await promise_rejects_js(
diff --git a/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js b/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js
index 85cd19a566..126fa90e16 100644
--- a/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js
@@ -5,3 +5,56 @@
'use strict';
validateInputFromAnotherBuilder('clamp');
+
+validateUnaryOperation(
+ 'clamp', allWebNNOperandDataTypes, /*alsoBuildActivation=*/ true);
+
+promise_test(async t => {
+ const options = {minValue: 1.0, maxValue: 3.0};
+ const input =
+ builder.input('input', {dataType: 'uint32', dimensions: [1, 2, 3]});
+ const output = builder.clamp(input, options);
+ assert_equals(output.dataType(), 'uint32');
+ assert_array_equals(output.shape(), [1, 2, 3]);
+}, '[clamp] Test building an operator with options');
+
+promise_test(async t => {
+ const options = {minValue: 0, maxValue: 0};
+ const input =
+ builder.input('input', {dataType: 'int32', dimensions: [1, 2, 3, 4]});
+ const output = builder.clamp(input, options);
+ assert_equals(output.dataType(), 'int32');
+ assert_array_equals(output.shape(), [1, 2, 3, 4]);
+}, '[clamp] Test building an operator with options.minValue == options.maxValue');
+
+promise_test(async t => {
+ const options = {minValue: 2.0};
+ builder.clamp(options);
+}, '[clamp] Test building an activation with options');
+
+promise_test(async t => {
+ const options = {minValue: 3.0, maxValue: 1.0};
+ const input =
+ builder.input('input', {dataType: 'uint8', dimensions: [1, 2, 3]});
+ assert_throws_js(TypeError, () => builder.clamp(input, options));
+}, '[clamp] Throw if options.minValue > options.maxValue when building an operator');
+
+// To be removed once infinite `minValue` is allowed. Tracked in
+// https://github.com/webmachinelearning/webnn/pull/647.
+promise_test(async t => {
+ const options = {minValue: -Infinity};
+ const input = builder.input('input', {dataType: 'float16', dimensions: []});
+ assert_throws_js(TypeError, () => builder.clamp(input, options));
+}, '[clamp] Throw if options.minValue is -Infinity when building an operator');
+
+promise_test(async t => {
+ const options = {minValue: 2.0, maxValue: -1.0};
+ assert_throws_js(TypeError, () => builder.clamp(options));
+}, '[clamp] Throw if options.minValue > options.maxValue when building an activation');
+
+// To be removed once NaN `maxValue` is allowed. Tracked in
+// https://github.com/webmachinelearning/webnn/pull/647.
+promise_test(async t => {
+ const options = {maxValue: NaN};
+ assert_throws_js(TypeError, () => builder.clamp(options));
+}, '[clamp] Throw if options.maxValue is NaN when building an activation');
diff --git a/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js b/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js
index ffc9c2c65d..7dac654951 100644
--- a/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js
@@ -55,3 +55,478 @@ multi_builder_test(async (t, builder, otherBuilder) => {
const filter = builder.input('filter', kExampleFilterDescriptor);
assert_throws_js(TypeError, () => builder.conv2d(input, filter, options));
}, '[conv2d] throw if activation option is from another builder');
+
+const tests = [
+ {
+ name: '[conv2d] Test with default options.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ output: {dataType: 'float32', dimensions: [1, 1, 3, 3]}
+ },
+ {
+ name: '[conv2d] Test with padding.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [1, 1, 1, 1],
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 5, 5]}
+ },
+ {
+ name: '[conv2d] Test with strides and padding.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 3, 3]}
+ },
+ {
+ name: '[conv2d] Test with strides and asymmetric padding.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 4, 2]},
+ options: {
+ padding: [1, 2, 0, 1],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 3, 3]}
+ },
+ {
+ name: '[conv2d] Test depthwise conv2d by setting groups to input channels.',
+ input: {dataType: 'float32', dimensions: [1, 4, 2, 2]},
+ filter: {dataType: 'float32', dimensions: [4, 1, 2, 2]},
+ options: {
+ groups: 4,
+ },
+ output: {dataType: 'float32', dimensions: [1, 4, 1, 1]}
+ },
+ {
+ name:
+ '[conv2d] Test depthwise conv2d with groups, inputLayout="nhwc" and filterLayout="ihwo".',
+ input: {dataType: 'float32', dimensions: [1, 2, 2, 4]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 2, 4]},
+ options: {
+ groups: 4,
+ inputLayout: 'nhwc',
+ filterLayout: 'ihwo',
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 1, 4]}
+ },
+ {
+ name:
+ '[conv2d] Test with dilations, inputLayout="nhwc" and filterLayout="ihwo".',
+ input: {dataType: 'float32', dimensions: [1, 65, 65, 1]},
+ filter: {dataType: 'float32', dimensions: [1, 3, 3, 1]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'ihwo',
+ dilations: [4, 4],
+ },
+ output: {dataType: 'float32', dimensions: [1, 57, 57, 1]}
+ },
+ {
+ name: '[conv2d] Test with inputLayout="nchw" and filterLayout="oihw".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ inputLayout: 'nchw',
+ filterLayout: 'oihw',
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 3, 3]}
+ },
+ {
+ name: '[conv2d] Test with inputLayout="nchw" and filterLayout="hwio".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]},
+ options: {
+ inputLayout: 'nchw',
+ filterLayout: 'hwio',
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 3, 3]}
+ },
+ {
+ name: '[conv2d] Test with inputLayout="nchw" and filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 3, 3, 2]},
+ options: {
+ inputLayout: 'nchw',
+ filterLayout: 'ohwi',
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 3, 3]}
+ },
+ {
+ name: '[conv2d] Test with inputLayout="nchw" and filterLayout="ihwo".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]},
+ options: {
+ inputLayout: 'nchw',
+ filterLayout: 'ihwo',
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 3, 3]}
+ },
+ {
+ name: '[conv2d] Test with inputLayout="nhwc" and filterLayout="oihw".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'oihw',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 1]}
+ },
+ {
+ name: '[conv2d] Test with inputLayout="nhwc" and filterLayout="hwio".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'hwio',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 1]}
+ },
+ {
+ name: '[conv2d] Test with inputLayout="nhwc" and filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 3, 3, 2]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'ohwi',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 1]}
+ },
+ {
+ name: '[conv2d] Test with inputLayout="nhwc" and filterLayout="ihwo".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'ihwo',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 1]}
+ },
+ {
+ name: '[conv2d] Throw if the input is not a 4-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 2, 1]},
+ },
+ {
+ name: '[conv2d] Throw if the input data type is not floating point.',
+ input: {dataType: 'int32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'int32', dimensions: [1, 1, 2, 2]},
+ },
+ {
+ name: '[conv2d] Throw if the filter is not a 4-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [2, 2]},
+ },
+ {
+ name:
+ '[conv2d] Throw if the filter data type doesn\'t match the input data type.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'int32', dimensions: [1, 1, 2, 2]},
+ },
+ {
+ name: '[conv2d] Throw if the length of padding is not 4.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ padding: [2, 2],
+ },
+ },
+ {
+ name: '[conv2d] Throw if the length of strides is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ strides: [2],
+ },
+ },
+ {
+ name: '[conv2d] Throw if strideHeight is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ strides: [0, 1],
+ },
+ },
+ {
+ name: '[conv2d] Throw if strideWidth is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ strides: [1, 0],
+ },
+ },
+ {
+ name: '[conv2d] Throw if the length of dilations is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ dilations: [1],
+ },
+ },
+ {
+ name: '[conv2d] Throw if dilationHeight is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ dilations: [0, 1],
+ },
+ },
+ {
+ name: '[conv2d] Throw if dilationWidth is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ dilations: [1, 0],
+ },
+ },
+ {
+ name: '[conv2d] Throw if inputChannels % groups is not 0.',
+ input: {dataType: 'float32', dimensions: [1, 4, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ groups: 3,
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels.',
+ input: {dataType: 'float32', dimensions: [1, 4, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ groups: 2,
+ },
+ },
+ {
+ name: '[conv2d] Throw if the groups is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 4, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ groups: 0,
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw due to overflow when calculating the effective filter height.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 434983, 2]},
+ options: {
+ dilations: [328442, 1],
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw due to overflow when calculating the effective filter width.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 234545]},
+ options: {
+ dilations: [2, 843452],
+ },
+ },
+ {
+ name: '[conv2d] Throw due to overflow when dilation height is too large.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ dilations: [kMaxUnsignedLong, 1],
+ },
+ },
+ {
+ name: '[conv2d] Throw due to overflow when dilation width is too large.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ dilations: [1, kMaxUnsignedLong],
+ },
+ },
+ {
+ name: '[conv2d] Throw due to underflow when calculating the output height.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 4, 2]},
+ options: {
+ dilations: [4, 1],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ },
+ },
+ {
+ name: '[conv2d] Throw due to underflow when calculating the output width.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 8]},
+ options: {
+ dilations: [1, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ },
+ },
+ {
+ name: '[conv2d] Throw if the bias is not a 1-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ bias: {dataType: 'float32', dimensions: [1, 2]},
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="oihw".',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="hwio".',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [2, 2, 1, 1]},
+ options: {
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 2, 1]},
+ options: {
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="ihwo".',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 2, 1]},
+ options: {
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if the bias data type doesn\'t match input data type.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ bias: {dataType: 'int32', dimensions: [1]},
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nchw" and filterLayout="oihw".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ inputLayout: 'nchw',
+ filterLayout: 'oihw',
+ groups: 2,
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nchw" and filterLayout="hwio".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]},
+ options: {
+ inputLayout: 'nchw',
+ filterLayout: 'hwio',
+ groups: 2,
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nchw" and filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 3, 3, 2]},
+ options: {
+ inputLayout: 'nchw',
+ filterLayout: 'ohwi',
+ groups: 2,
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nchw" and filterLayout="ihwo".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]},
+ options: {
+ inputLayout: 'nchw',
+ filterLayout: 'ihwo',
+ groups: 2,
+ },
+
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nhwc" and filterLayout="oihw".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'oihw',
+ groups: 2,
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nhwc" and filterLayout="hwio".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'hwio',
+ groups: 2,
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nhwc" and filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 3, 3, 2]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'ohwi',
+ groups: 2,
+ },
+ },
+ {
+ name:
+ '[conv2d] Throw if inputChannels / groups is not equal to filterInputChannels with inputLayout="nhwc" and filterLayout="ihwo".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'ihwo',
+ groups: 2,
+ },
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ const filter = builder.input(
+ 'filter',
+ {dataType: test.filter.dataType, dimensions: test.filter.dimensions});
+
+ if (test.options && test.options.bias) {
+ test.options.bias = builder.input('bias', {
+ dataType: test.options.bias.dataType,
+ dimensions: test.options.bias.dimensions
+ });
+ }
+
+ if (test.output) {
+ const output = builder.conv2d(input, filter, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.conv2d(input, filter, test.options));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js b/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js
index c14f445bf3..02822c5274 100644
--- a/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js
@@ -57,3 +57,473 @@ multi_builder_test(async (t, builder, otherBuilder) => {
assert_throws_js(
TypeError, () => builder.convTranspose2d(input, filter, options));
}, '[convTranspose2d] throw if activation option is from another builder');
+
+const tests = [
+ {
+ name: '[convTranspose2d] Test with default options.',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ output: {dataType: 'float32', dimensions: [1, 1, 5, 5]}
+ },
+ {
+ name:
+ '[convTranspose2d] Test with inputLayout="nchw" and filterLayout="hwoi".',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]},
+ options: {
+ filterLayout: 'hwoi',
+ inputLayout: 'nchw',
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 5, 5]}
+ },
+ {
+ name:
+ '[convTranspose2d] Test with inputLayout="nchw" and filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]},
+ options: {
+ filterLayout: 'ohwi',
+ inputLayout: 'nchw',
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 5, 5]}
+ },
+ {
+ name:
+ '[convTranspose2d] Test with inputLayout="nhwc" and filterLayout="iohw".',
+ input: {dataType: 'float32', dimensions: [1, 3, 3, 1]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ filterLayout: 'iohw',
+ inputLayout: 'nhwc',
+ },
+ output: {dataType: 'float32', dimensions: [1, 5, 5, 2]}
+ },
+ {
+ name:
+ '[convTranspose2d] Test with inputLayout="nhwc" and filterLayout="hwoi".',
+ input: {dataType: 'float32', dimensions: [1, 3, 3, 1]},
+ filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]},
+ options: {
+ filterLayout: 'hwoi',
+ inputLayout: 'nhwc',
+ },
+ output: {dataType: 'float32', dimensions: [1, 5, 5, 2]}
+ },
+ {
+ name:
+ '[convTranspose2d] Test with inputLayout="nhwc" and filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 3, 3, 1]},
+ filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]},
+ options: {
+ filterLayout: 'ohwi',
+ inputLayout: 'nhwc',
+ },
+ output: {dataType: 'float32', dimensions: [1, 5, 5, 2]}
+ },
+ {
+ name: '[convTranspose2d] Test with strides=[3, 2], outputSizes=[10, 8].',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ strides: [3, 2],
+ outputSizes: [10, 8],
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 10, 8]}
+ },
+ {
+ name: '[convTranspose2d] Test with strides=[3, 2], outputPadding=[1, 1].',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ strides: [3, 2],
+ outputPadding: [1, 1],
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 10, 8]}
+ },
+ {
+ name: '[convTranspose2d] Test with padding=1.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [1, 1, 1, 1],
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 5, 5]}
+ },
+ {
+ name: '[convTranspose2d] Test with padding=1, groups=3.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [1, 1, 1, 1],
+ groups: 3,
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 5, 5]}
+ },
+ {
+ name: '[convTranspose2d] Test with strides=2.',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 7, 7]}
+ },
+ {
+ name: '[convTranspose2d] Test with strides=2 and padding=1.',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 1, 5, 5]}
+ },
+ {
+ name:
+ '[convTranspose2d] Test when the output sizes are explicitly specified, the output padding values are ignored though padding value is not smaller than stride along the same axis.',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ outputPadding: [3, 3],
+ strides: [3, 2],
+ outputSizes: [10, 8],
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 10, 8]}
+ },
+ {
+ name: '[convTranspose2d] Throw if the input is not a 4-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the input data type is not floating point.',
+ input: {dataType: 'int32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'int32', dimensions: [1, 1, 2, 2]},
+ },
+ {
+ name: '[convTranspose2d] Throw if the filter is not a 4-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [2, 2]},
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the filter data type doesn\'t match the input data type.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'int32', dimensions: [1, 1, 2, 2]},
+ },
+ {
+ name: '[convTranspose2d] Throw if the length of padding is not 4.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ padding: [2, 2],
+ },
+ },
+ {
+ name: '[convTranspose2d] Throw if the length of strides is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ strides: [2],
+ },
+ },
+ {
+ name: '[convTranspose2d] Throw if one stride value is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ strides: [1, 0],
+ },
+ },
+ {
+ name: '[convTranspose2d] Throw if the length of dilations is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ dilations: [1],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the one dilation value is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ dilations: [1, 0],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nchw" and filterLayout="iohw".',
+ input: {dataType: 'float32', dimensions: [1, 3, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ filterLayout: 'iohw',
+ inputLayout: 'nchw',
+ groups: 1,
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nchw" and filterLayout="hwoi".',
+ input: {dataType: 'float32', dimensions: [1, 3, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [3, 1, 2, 1]},
+ options: {
+ filterLayout: 'hwoi',
+ inputLayout: 'nchw',
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nchw" and filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]},
+ options: {
+ filterLayout: 'ohwi',
+ inputLayout: 'nchw',
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nhwc" and filterLayout="iohw".',
+ input: {dataType: 'float32', dimensions: [1, 3, 3, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ filterLayout: 'iohw',
+ inputLayout: 'nhwc',
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the input channels is not equal to the filter input channels inputLayout="nhwc" and filterLayout="hwoi".',
+ input: {dataType: 'float32', dimensions: [1, 3, 3, 2]},
+ filter: {dataType: 'float32', dimensions: [3, 3, 2, 1]},
+ options: {
+ filterLayout: 'hwoi',
+ inputLayout: 'nhwc',
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the input channels is not equal to the filter input channels with inputLayout="nhwc" and filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 3, 3, 2]},
+ filter: {dataType: 'float32', dimensions: [2, 3, 3, 1]},
+ options: {
+ filterLayout: 'ohwi',
+ inputLayout: 'nhwc',
+ },
+ },
+ {
+ name: '[convTranspose2d] Throw if output channels is too large.',
+ input: {dataType: 'float32', dimensions: [1, 4, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [4, 2, 2, 2]},
+ options: {
+ groups: kMaxUnsignedLong,
+ },
+ },
+ {
+ name: '[convTranspose2d] Throw if the groups is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 4, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ groups: 0,
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw due to overflow when calculating the effective filter height.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 434983, 2]},
+ options: {
+ dilations: [328443, 1],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw due to overflow when calculating the effective filter width.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 234545]},
+ options: {
+ dilations: [2, 843452],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw due to overflow when dilation height is too large.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 2]},
+ options: {
+ dilations: [kMaxUnsignedLong, 1],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw due to overflow when dilation width is too large.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 2]},
+ options: {
+ dilations: [1, kMaxUnsignedLong],
+ },
+ },
+ {
+ name: '[convTranspose2d] Throw if the bias is not a 1-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ bias: {dataType: 'float32', dimensions: [1, 2]},
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="iohw".',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ filterLayout: 'iohw',
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="hwoi".',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [2, 2, 1, 1]},
+ options: {
+ filterLayout: 'hwoi',
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the bias shape is not equal to [output_channels] with filterLayout="ohwi".',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 2, 1]},
+ options: {
+ filterLayout: 'ohwi',
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the bias data type doesn\'t match input data type.',
+ input: {dataType: 'float32', dimensions: [1, 1, 5, 5]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ options: {
+ bias: {dataType: 'int32', dimensions: [1]},
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the outputPadding is not a sequence of length 2.',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ strides: [3, 2],
+ outputPadding: [1, 1, 1, 1],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the outputPadding is not smaller than stride along the width dimension.',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [0, 0, 3, 3],
+ strides: [2, 2],
+ outputPadding: [0, 2],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the outputPadding is not smaller than stride along the height dimension.',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [0, 0, 3, 3],
+ strides: [2, 2],
+ outputPadding: [2, 0],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw if the outputSizes is not a sequence of length 2.',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 2, 3, 3]},
+ options: {
+ strides: [3, 2],
+ outputSizes: [1, 2, 10, 8],
+ },
+ },
+ {
+ name: '[convTranspose2d] Throw if the padding height is too large.',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [4, 4, 0, 0],
+ strides: [2, 2],
+ outputPadding: [1, 0],
+ },
+ },
+ {
+ name: '[convTranspose2d] Throw if the padding width is too large.',
+ input: {dataType: 'float32', dimensions: [1, 1, 2, 2]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [0, 0, 4, 4],
+ strides: [2, 2],
+ outputPadding: [0, 1],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw due to outputSizes values are smaller than the output sizes calculated by not using outputPadding.',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ outputSizes: [4, 4],
+ outputPadding: [1, 1],
+ },
+ },
+ {
+ name:
+ '[convTranspose2d] Throw due to outputSizes values are greater than the output sizes calculated by not using outputPadding.',
+ input: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ filter: {dataType: 'float32', dimensions: [1, 1, 3, 3]},
+ options: {
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ outputSizes: [6, 8],
+ outputPadding: [1, 1],
+ },
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ const filter = builder.input(
+ 'filter',
+ {dataType: test.filter.dataType, dimensions: test.filter.dimensions});
+
+ if (test.options && test.options.bias) {
+ test.options.bias = builder.input('bias', {
+ dataType: test.options.bias.dataType,
+ dimensions: test.options.bias.dimensions
+ });
+ }
+
+ if (test.output) {
+ const output = builder.convTranspose2d(input, filter, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError,
+ () => builder.convTranspose2d(input, filter, test.options));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js
index 6e842cb691..53ec5e54ae 100644
--- a/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js
@@ -5,3 +5,43 @@
'use strict';
validateInputFromAnotherBuilder('elu');
+
+validateUnaryOperation(
+ 'elu', floatingPointTypes, /*alsoBuildActivation=*/ true);
+
+promise_test(async t => {
+ const options = {alpha: 1.0};
+ const input =
+ builder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]});
+ const output = builder.elu(input, options);
+ assert_equals(output.dataType(), 'float32');
+ assert_array_equals(output.shape(), [1, 2, 3]);
+}, '[elu] Test building an operator with options');
+
+promise_test(async t => {
+ const options = {alpha: 1.5};
+ builder.elu(options);
+}, '[elu] Test building an activation with options');
+
+promise_test(async t => {
+ const options = {alpha: -1.0};
+ const input =
+ builder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]});
+ assert_throws_js(TypeError, () => builder.elu(input, options));
+}, '[elu] Throw if options.alpha <= 0 when building an operator');
+
+promise_test(async t => {
+ const options = {alpha: NaN};
+ const input = builder.input('input', {dataType: 'float16', dimensions: []});
+ assert_throws_js(TypeError, () => builder.elu(input, options));
+}, '[elu] Throw if options.alpha is NaN when building an operator');
+
+promise_test(async t => {
+ const options = {alpha: 0};
+ assert_throws_js(TypeError, () => builder.elu(options));
+}, '[elu] Throw if options.alpha <= 0 when building an activation');
+
+promise_test(async t => {
+ const options = {alpha: Infinity};
+ assert_throws_js(TypeError, () => builder.elu(options));
+}, '[elu] Throw if options.alpha is Infinity when building an activation');
diff --git a/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js b/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js
index d90ab89468..088d826df7 100644
--- a/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js
@@ -12,3 +12,66 @@ multi_builder_test(async (t, builder, otherBuilder) => {
assert_throws_js(
TypeError, () => builder.expand(inputFromOtherBuilder, newShape));
}, '[expand] throw if input is from another builder');
+
+const tests = [
+ {
+ name: '[expand] Test with 0-D scalar to 3-D tensor.',
+ input: {dataType: 'float32', dimensions: []},
+ newShape: [3, 4, 5],
+ output: {dataType: 'float32', dimensions: [3, 4, 5]}
+ },
+ {
+ name: '[expand] Test with the new shapes that are the same as input.',
+ input: {dataType: 'float32', dimensions: [4]},
+ newShape: [4],
+ output: {dataType: 'float32', dimensions: [4]}
+ },
+ {
+ name: '[expand] Test with the new shapes that are broadcastable.',
+ input: {dataType: 'int32', dimensions: [3, 1, 5]},
+ newShape: [3, 4, 5],
+ output: {dataType: 'int32', dimensions: [3, 4, 5]}
+ },
+ {
+ name:
+ '[expand] Test with the new shapes that are broadcastable and the rank of new shapes is larger than input.',
+ input: {dataType: 'int32', dimensions: [2, 5]},
+ newShape: [3, 2, 5],
+ output: {dataType: 'int32', dimensions: [3, 2, 5]}
+ },
+ {
+ name:
+ '[expand] Throw if the input shapes are the same rank but not broadcastable.',
+ input: {dataType: 'uint32', dimensions: [3, 6, 2]},
+ newShape: [4, 3, 5],
+ },
+ {
+ name: '[expand] Throw if the input shapes are not broadcastable.',
+ input: {dataType: 'uint32', dimensions: [5, 4]},
+ newShape: [5],
+ },
+ {
+ name: '[expand] Throw if the number of new shapes is too large.',
+ input: {dataType: 'float32', dimensions: [1, 2, 1, 1]},
+ newShape: [1, 2, kMaxUnsignedLong, kMaxUnsignedLong],
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ const options = {};
+ if (test.axis) {
+ options.axis = test.axis;
+ }
+
+ if (test.output) {
+ const output = builder.expand(input, test.newShape);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(TypeError, () => builder.expand(input, test.newShape));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/gelu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gelu.https.any.js
new file mode 100644
index 0000000000..c758c61f4c
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/gelu.https.any.js
@@ -0,0 +1,10 @@
+// META: title=validation tests for WebNN API gelu operation
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+'use strict';
+
+validateInputFromAnotherBuilder('gelu');
+
+validateUnaryOperation(
+ 'gelu', floatingPointTypes, /*alsoBuildActivation=*/ true);
diff --git a/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js
index 77ce6383cc..abe0ba6193 100644
--- a/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js
@@ -19,3 +19,143 @@ multi_builder_test(async (t, builder, otherBuilder) => {
const b = builder.input('b', kExampleInputDescriptor);
assert_throws_js(TypeError, () => builder.gemm(a, b, options));
}, '[gemm] throw if c option is from another builder');
+
+const tests = [
+ {
+ name: '[gemm] Test building gemm with default option.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [3, 4]},
+ output: {dataType: 'float32', dimensions: [2, 4]}
+ },
+ {
+ name:
+ '[gemm] Throw if inputShapeA[1] is not equal to inputShapeB[0] default options.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [2, 4]},
+ },
+ {
+ name: '[gemm] Test building gemm with aTranspose=true.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [2, 4]},
+ options: {
+ aTranspose: true,
+ },
+ output: {dataType: 'float32', dimensions: [3, 4]}
+ },
+ {
+ name:
+ '[gemm] Throw if inputShapeA[0] is not equal to inputShapeB[0] with aTranspose=true.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [3, 4]},
+ options: {
+ aTranspose: true,
+ },
+ },
+ {
+ name: '[gemm] Test building gemm with bTranspose=true.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [4, 3]},
+ options: {
+ bTranspose: true,
+ },
+ output: {dataType: 'float32', dimensions: [2, 4]}
+ },
+ {
+ name:
+ '[gemm] Throw if inputShapeA[0] is not equal to inputShapeB[0] with bTranspose=true.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [3, 4]},
+ options: {
+ bTranspose: true,
+ },
+ },
+ {
+ name: '[gemm] Throw if the rank of inputA is not 2.',
+ a: {dataType: 'float32', dimensions: [2, 3, 1]},
+ b: {dataType: 'float32', dimensions: [2, 4]},
+ },
+ {
+ name: '[gemm] Throw if the rank of inputB is not 2.',
+ a: {dataType: 'float32', dimensions: [2, 4]},
+ b: {dataType: 'float32', dimensions: [2, 3, 1]},
+ },
+ {
+ name: '[gemm] Throw if data types of two inputs do not match.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float16', dimensions: [3, 4]},
+ },
+ {
+ name: '[gemm] Test building gemm with inputC.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [3, 4]},
+ options: {
+ c: {dataType: 'float32', dimensions: [4]},
+ },
+ output: {dataType: 'float32', dimensions: [2, 4]}
+ },
+ {
+ name: '[gemm] Test building gemm with scalar inputC.',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [3, 4]},
+ options: {
+ c: {dataType: 'float32', dimensions: []},
+ },
+ output: {dataType: 'float32', dimensions: [2, 4]}
+ },
+ {
+ name:
+ '[gemm] Throw if inputShapeC is not unidirectionally broadcastable to the output shape [inputShapeA[0], inputShapeB[1]].',
+ a: {dataType: 'float32', dimensions: [2, 3]},
+ b: {dataType: 'float32', dimensions: [3, 4]},
+ options: {
+ c: {dataType: 'float32', dimensions: [2, 3]},
+ },
+ },
+ {
+ name: '[gemm] Throw if the input data type is not floating point.',
+ a: {dataType: 'int32', dimensions: [2, 3]},
+ b: {dataType: 'int32', dimensions: [3, 4]}
+ },
+ {
+ name:
+ '[gemm] Throw if data type of inputC does not match ones of inputA and inputB.',
+ a: {dataType: 'float32', dimensions: [3, 2]},
+ b: {dataType: 'float32', dimensions: [4, 3]},
+ options: {
+ c: {dataType: 'float16', dimensions: [2, 4]},
+ aTranspose: true,
+ bTranspose: true,
+ },
+ },
+ {
+ name: '[gemm] Throw if the rank of inputC is 3.',
+ a: {dataType: 'float32', dimensions: [3, 2]},
+ b: {dataType: 'float32', dimensions: [4, 3]},
+ options: {
+ c: {dataType: 'float32', dimensions: [2, 3, 4]},
+ aTranspose: true,
+ bTranspose: true,
+ },
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const a = builder.input(
+ 'a', {dataType: test.a.dataType, dimensions: test.a.dimensions});
+ const b = builder.input(
+ 'b', {dataType: test.b.dataType, dimensions: test.b.dimensions});
+ if (test.options && test.options.c) {
+ test.options.c = builder.input('c', {
+ dataType: test.options.c.dataType,
+ dimensions: test.options.c.dimensions
+ });
+ }
+ if (test.output) {
+ const output = builder.gemm(a, b, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(TypeError, () => builder.gemm(a, b, test.options));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js b/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js
index 01b24dbc7c..2c55d0eb9d 100644
--- a/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js
@@ -5,3 +5,31 @@
'use strict';
validateInputFromAnotherBuilder('hardSigmoid');
+
+validateUnaryOperation(
+ 'hardSigmoid', floatingPointTypes, /*alsoBuildActivation=*/ true);
+
+promise_test(async t => {
+ const options = {alpha: 0.5, beta: 1.0};
+ const input =
+ builder.input('input', {dataType: 'float16', dimensions: [1, 2, 3]});
+ const output = builder.hardSigmoid(input, options);
+ assert_equals(output.dataType(), 'float16');
+ assert_array_equals(output.shape(), [1, 2, 3]);
+}, '[hardSigmoid] Test building an operator with options');
+
+promise_test(async t => {
+ const options = {alpha: 0.2};
+ builder.hardSigmoid(options);
+}, '[hardSigmoid] Test building an activation with options');
+
+promise_test(async t => {
+ const options = {beta: NaN};
+ const input = builder.input('input', {dataType: 'float32', dimensions: []});
+ assert_throws_js(TypeError, () => builder.hardSigmoid(input, options));
+}, '[hardSigmoid] Throw if options.beta is NaN when building an operator');
+
+promise_test(async t => {
+ const options = {alpha: Infinity};
+ assert_throws_js(TypeError, () => builder.hardSigmoid(options));
+}, '[hardSigmoid] Throw if options.alpha is Infinity when building an activation');
diff --git a/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js
index bdd338588f..4fc26ec5ae 100644
--- a/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js
@@ -41,3 +41,152 @@ multi_builder_test(async (t, builder, otherBuilder) => {
assert_throws_js(
TypeError, () => builder.instanceNormalization(input, options));
}, '[instanceNormalization] throw if bias option is from another builder');
+
+const tests = [
+ {
+ name: '[instanceNormalization] Test with default options for 4-D input.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4]}
+ },
+ {
+ name:
+ '[instanceNormalization] Test with scale, bias and default epsilon value.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [2]},
+ bias: {dataType: 'float32', dimensions: [2]},
+ epsilon: 1e-5,
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4]}
+ },
+ {
+ name: '[instanceNormalization] Test with a non-default epsilon value.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ epsilon: 1e-4,
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4]}
+ },
+ {
+ name: '[instanceNormalization] Test with layout=nhwc.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ layout: 'nhwc',
+ scale: {dataType: 'float32', dimensions: [4]},
+ bias: {dataType: 'float32', dimensions: [4]},
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4]}
+ },
+ {
+ name: '[instanceNormalization] Test when the input data type is float16.',
+ input: {dataType: 'float16', dimensions: [1, 2, 3, 4]},
+ output: {dataType: 'float16', dimensions: [1, 2, 3, 4]}
+ },
+ {
+ name: '[instanceNormalization] Throw if the input is not a 4-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5, 2]},
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the input data type is not one of floating point types.',
+ input: {dataType: 'int32', dimensions: [1, 2, 5, 5]},
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the scale data type is not the same as the input data type.',
+ input: {dataType: 'float16', dimensions: [1, 2, 5, 5]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the scale operand is not a 1-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [2, 1]},
+ },
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the size of scale operand is not equal to the size of the feature dimension of the input with layout=nhwc.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ layout: 'nhwc',
+ scale: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the size of scale operand is not equal to the size of the feature dimension of the input with layout=nchw.',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ options: {
+ layout: 'nchw',
+ scale: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the bias data type is not the same as the input data type.',
+ input: {dataType: 'float16', dimensions: [1, 2, 5, 5]},
+ options: {
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the bias operand is not a 1-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [2, 1]},
+ },
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the size of bias operand is not equal to the size of the feature dimension of the input with layout=nhwc.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ layout: 'nhwc',
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+ {
+ name:
+ '[instanceNormalization] Throw if the size of bias operand is not equal to the size of the feature dimension of the input with layout=nchw.',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ options: {
+ layout: 'nchw',
+ bias: {dataType: 'float32', dimensions: [2]},
+ },
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+
+ if (test.options && test.options.bias) {
+ test.options.bias = builder.input('bias', {
+ dataType: test.options.bias.dataType,
+ dimensions: test.options.bias.dimensions
+ });
+ }
+ if (test.options && test.options.scale) {
+ test.options.scale = builder.input('scale', {
+ dataType: test.options.scale.dataType,
+ dimensions: test.options.scale.dimensions
+ });
+ }
+
+ if (test.output) {
+ const output = builder.instanceNormalization(input, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError,
+ () => builder.instanceNormalization(input, test.options));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js
index e9e9141aa6..63f9c0dbc5 100644
--- a/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js
@@ -9,8 +9,6 @@ const kExampleInputDescriptor = {
dimensions: [2, 2]
};
-validateOptionsAxes('layerNormalization', 4);
-
validateInputFromAnotherBuilder('layerNormalization');
multi_builder_test(async (t, builder, otherBuilder) => {
@@ -30,3 +28,181 @@ multi_builder_test(async (t, builder, otherBuilder) => {
const input = builder.input('input', kExampleInputDescriptor);
assert_throws_js(TypeError, () => builder.layerNormalization(input, options));
}, '[layerNormalization] throw if bias option is from another builder');
+
+const tests = [
+ {
+ name: '[layerNormalization] Test with default options for scalar input.',
+ input: {dataType: 'float32', dimensions: []},
+ output: {dataType: 'float32', dimensions: []},
+ },
+ {
+ name: '[layerNormalization] Test when the input data type is float16.',
+ input: {dataType: 'float16', dimensions: []},
+ output: {dataType: 'float16', dimensions: []},
+ },
+ {
+ name: '[layerNormalization] Test with given axes.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ axes: [3],
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ },
+ {
+ name: '[layerNormalization] Test with given scale.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [2, 3, 4]},
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ },
+ {
+ name: '[layerNormalization] Test with a non-default epsilon value.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ epsilon: 1e-4, // default epsilon=1e-5
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ },
+ {
+ name: '[layerNormalization] Test with given axes, scale and bias.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [3, 4]},
+ bias: {dataType: 'float32', dimensions: [3, 4]},
+ axes: [2, 3],
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ },
+ {
+ name: '[layerNormalization] Test with nonconsecutive axes.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4, 5, 6]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [2, 4, 6]},
+ bias: {dataType: 'float32', dimensions: [2, 4, 6]},
+ axes: [1, 3, 5],
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4, 5, 6]},
+ },
+ {
+ name: '[layerNormalization] Test with axes in descending order.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4, 5, 6]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [6, 5, 4, 3, 2]},
+ bias: {dataType: 'float32', dimensions: [6, 5, 4, 3, 2]},
+ axes: [5, 4, 3, 2, 1]
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 4, 5, 6]},
+ },
+ {
+ name:
+ '[layerNormalization] Throw if the input data type is not one of the floating point types.',
+ input: {dataType: 'uint32', dimensions: [1, 2, 3, 4]},
+ },
+ {
+ name:
+ '[layerNormalization] Throw if the axis is greater than the input rank.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ axes: [1, 2, 4],
+ },
+ },
+ {
+ name: '[layerNormalization] Throw if the axes have duplications.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {axes: [3, 3]},
+ },
+ {
+ name:
+ '[layerNormalization] Throw if the bias data type doesn\'t match input data type',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ scale: {dataType: 'float32', dimensions: [3, 4]},
+ bias: {dataType: 'float16', dimensions: [3, 4]},
+ axes: [2, 3],
+ },
+ },
+ {
+ name:
+ '[layerNormalization] Throw if the scale data type doesn\'t match input data type',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ scale: {dataType: 'float16', dimensions: [3, 4]},
+ bias: {dataType: 'float32', dimensions: [3, 4]},
+ axes: [2, 3],
+ },
+ },
+ {
+ name:
+ '[layerNormalization] Throw if the bias dimensions doesn\'t match axis dimensions.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ bias: {
+ dataType: 'float32',
+ dimensions: [3, 3, 4]
+ }, // for 4D input, default axes = [1,2,3]
+ },
+ },
+ {
+ name:
+ '[layerNormalization] Throw if the scale dimensions doesn\'t match axis dimensions.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ scale: {
+ dataType: 'float32',
+ dimensions: [3, 3, 4]
+ }, // for 4D input, default axes = [1,2,3]
+ },
+ },
+ {
+ name:
+ '[layerNormalization] Throw if the bias rank doesn\'t match axis rank.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ bias: {
+ dataType: 'float32',
+ dimensions: [1, 2, 3, 4]
+ }, // for 4D input, default axes = [1,2,3]
+ },
+ },
+ {
+ name:
+ '[layerNormalization] Throw if the scale rank doesn\'t match axis rank.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {
+ scale: {
+ dataType: 'float32',
+ dimensions: [1, 2, 3, 4]
+ }, // for 4D input, default axes = [1,2,3]
+ },
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+
+ if (test.options && test.options.bias) {
+ test.options.bias = builder.input('bias', {
+ dataType: test.options.bias.dataType,
+ dimensions: test.options.bias.dimensions
+ });
+ }
+ if (test.options && test.options.scale) {
+ test.options.scale = builder.input('scale', {
+ dataType: test.options.scale.dataType,
+ dimensions: test.options.scale.dimensions
+ });
+ }
+
+ if (test.output) {
+ const output = builder.layerNormalization(input, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.layerNormalization(input, test.options));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js
index 6fc19b1f0d..f250b0eda6 100644
--- a/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js
@@ -5,3 +5,31 @@
'use strict';
validateInputFromAnotherBuilder('leakyRelu');
+
+validateUnaryOperation(
+ 'leakyRelu', floatingPointTypes, /*alsoBuildActivation=*/ true);
+
+promise_test(async t => {
+ const options = {alpha: 0.02};
+ const input =
+ builder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]});
+ const output = builder.leakyRelu(input, options);
+ assert_equals(output.dataType(), 'float32');
+ assert_array_equals(output.shape(), [1, 2, 3]);
+}, '[leakyRelu] Test building an operator with options');
+
+promise_test(async t => {
+ const options = {alpha: 0.03};
+ builder.leakyRelu(options);
+}, '[leakyRelu] Test building an activation with options');
+
+promise_test(async t => {
+ const options = {alpha: Infinity};
+ const input = builder.input('input', {dataType: 'float16', dimensions: []});
+ assert_throws_js(TypeError, () => builder.leakyRelu(input, options));
+}, '[leakyRelu] Throw if options.alpha is Infinity when building an operator');
+
+promise_test(async t => {
+ const options = {alpha: -NaN};
+ assert_throws_js(TypeError, () => builder.leakyRelu(options));
+}, '[leakyRelu] Throw if options.alpha is -NaN when building an activation');
diff --git a/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js b/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js
index 99c1daad3f..6ec0389fc3 100644
--- a/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js
@@ -5,3 +5,31 @@
'use strict';
validateInputFromAnotherBuilder('linear');
+
+validateUnaryOperation(
+ 'linear', floatingPointTypes, /*alsoBuildActivation=*/ true);
+
+promise_test(async t => {
+ const options = {alpha: 1.5, beta: 0.3};
+ const input =
+ builder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]});
+ const output = builder.linear(input, options);
+ assert_equals(output.dataType(), 'float32');
+ assert_array_equals(output.shape(), [1, 2, 3]);
+}, '[linear] Test building an operator with options');
+
+promise_test(async t => {
+ const options = {beta: 1.5};
+ builder.linear(options);
+}, '[linear] Test building an activation with options');
+
+promise_test(async t => {
+ const options = {beta: -Infinity};
+ const input = builder.input('input', {dataType: 'float16', dimensions: []});
+ assert_throws_js(TypeError, () => builder.linear(input, options));
+}, '[linear] Throw if options.beta is -Infinity when building an operator');
+
+promise_test(async t => {
+ const options = {alpha: NaN};
+ assert_throws_js(TypeError, () => builder.linear(options));
+}, '[linear] Throw if options.alpha is NaN when building an activation');
diff --git a/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js b/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js
index 03616ddb01..8db16242c9 100644
--- a/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js
@@ -5,3 +5,116 @@
'use strict';
validateTwoInputsFromMultipleBuilders('matmul');
+
+const tests = [
+ {
+ name: '[matmul] Throw if first input\'s rank is less than 2',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [2]},
+ b: {dataType: 'float32', dimensions: [2, 2]}
+ }
+ },
+ {
+ name: '[matmul] Throw if second input\'s rank is less than 2',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [2, 2]},
+ b: {dataType: 'float32', dimensions: [2]}
+ }
+ },
+ {
+ name: '[matmul] Test with 2-D input and 4-D input',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [1, 4]},
+ b: {dataType: 'float32', dimensions: [2, 2, 4, 2]}
+ },
+ output: {dataType: 'float32', dimensions: [2, 2, 1, 2]}
+ },
+ {
+ name: '[matmul] Test with 2-D input and 2-D input',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [4, 2]},
+ b: {dataType: 'float32', dimensions: [2, 3]}
+ },
+ output: {dataType: 'float32', dimensions: [4, 3]}
+ },
+ {
+ // batchShape is a clone of inputShape with the spatial dimensions
+ // (last 2 items) removed.
+ name:
+ '[matmul] Test with 3-D input and 3-D input of broadcastable batchShape',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [2, 3, 4]},
+ b: {dataType: 'float32', dimensions: [1, 4, 1]}
+ },
+ output: {dataType: 'float32', dimensions: [2, 3, 1]}
+ },
+ {
+ // batchShape is a clone of inputShape with the spatial dimensions
+ // (last 2 items) removed.
+ name:
+ '[matmul] Test with 4-D input and 3-D input of broadcastable batchShape',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [2, 2, 3, 4]},
+ b: {dataType: 'float32', dimensions: [1, 4, 5]}
+ },
+ output: {dataType: 'float32', dimensions: [2, 2, 3, 5]}
+ },
+ {
+ name: '[matmul] Test with 3-D input and 3-D input',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [2, 3, 4]},
+ b: {dataType: 'float32', dimensions: [2, 4, 5]}
+ },
+ output: {dataType: 'float32', dimensions: [2, 3, 5]}
+ },
+ {
+ name: '[matmul] Throw if the input data type is not floating point',
+ inputs: {
+ a: {dataType: 'uint32', dimensions: [2, 3, 4]},
+ b: {dataType: 'uint32', dimensions: [2, 4, 5]}
+ }
+ },
+ {
+ name: '[matmul] Throw if data type of two inputs don\'t match',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [2, 3, 4]},
+ b: {dataType: 'float16', dimensions: [2, 4, 5]}
+ }
+ },
+ {
+ name:
+ '[matmul] Throw if columns of first input\'s shape doesn\'t match the rows of second input\'s shape',
+ inputs: {
+ a: {dataType: 'float32', dimensions: /* [rows, columns] */[2, 3]},
+ b: {dataType: 'float32', dimensions: /* [rows, columns] */[2, 4]}
+ },
+ },
+ {
+ // batchShape is a clone of inputShape with the spatial dimensions
+ // (last 2 items) removed.
+ name: '[matmul] Throw if batchShapes aren\'t bidirectionally broadcastable',
+ inputs: {
+ a: {dataType: 'float32', dimensions: [3, 3, 4]},
+ b: {dataType: 'float32', dimensions: [2, 4, 1]}
+ },
+ },
+];
+
+tests.forEach(test => promise_test(async t => {
+ const inputA = builder.input('a', {
+ dataType: test.inputs.a.dataType,
+ dimensions: test.inputs.a.dimensions
+ });
+ const inputB = builder.input('b', {
+ dataType: test.inputs.b.dataType,
+ dimensions: test.inputs.b.dimensions
+ });
+ if (test.output) {
+ const output = builder.matmul(inputA, inputB);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.matmul(inputA, inputB));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js b/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js
index 11c6a8f7ef..cc39bee4c0 100644
--- a/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js
@@ -15,3 +15,73 @@ multi_builder_test(async (t, builder, otherBuilder) => {
() =>
builder.pad(inputFromOtherBuilder, beginningPadding, endingPadding));
}, '[pad] throw if input is from another builder');
+
+const tests = [
+ {
+ name:
+ '[pad] Test with default options, beginningPadding=[1, 2] and endingPadding=[1, 2].',
+ input: {dataType: 'float32', dimensions: [2, 3]},
+ beginningPadding: [1, 2],
+ endingPadding: [1, 2],
+ options: {
+ mode: 'constant',
+ value: 0,
+ },
+ output: {dataType: 'float32', dimensions: [4, 7]}
+ },
+ {
+ name: '[pad] Throw if building pad for scalar input.',
+ input: {dataType: 'float32', dimensions: []},
+ beginningPadding: [],
+ endingPadding: [],
+ },
+ {
+ name:
+ '[pad] Throw if the length of beginningPadding is not equal to the input rank.',
+ input: {dataType: 'float32', dimensions: [2, 3]},
+ beginningPadding: [1],
+ endingPadding: [1, 2],
+ options: {
+ mode: 'edge',
+ value: 0,
+ },
+ },
+ {
+ name:
+ '[pad] Throw if the length of endingPadding is not equal to the input rank.',
+ input: {dataType: 'float32', dimensions: [2, 3]},
+ beginningPadding: [1, 0],
+ endingPadding: [1, 2, 0],
+ options: {
+ mode: 'reflection',
+ },
+ },
+ {
+ name: '[pad] Throw if the padding of one dimension is too large.',
+ input: {dataType: 'float32', dimensions: [2, 3]},
+ beginningPadding: [2294967295, 0],
+ endingPadding: [3294967295, 2],
+ options: {
+ mode: 'reflection',
+ },
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ if (test.output) {
+ const output = builder.pad(
+ input, test.beginningPadding, test.endingPadding, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError,
+ () => builder.pad(
+ input, test.beginningPadding, test.endingPadding,
+ test.options));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/pooling-and-reduction-keep-dims.https.any.js b/testing/web-platform/tests/webnn/validation_tests/pooling-and-reduction-keep-dims.https.any.js
new file mode 100644
index 0000000000..9f6b9fb338
--- /dev/null
+++ b/testing/web-platform/tests/webnn/validation_tests/pooling-and-reduction-keep-dims.https.any.js
@@ -0,0 +1,94 @@
+// META: title=validation tests for pooling and reduction operators keep dimensions
+// META: global=window,dedicatedworker
+// META: script=../resources/utils.js
+// META: script=../resources/utils_validation.js
+// META: timeout=long
+
+'use strict';
+
+// This is used to reproduce an issue(crbug.com/331841268) of averagePool2d in
+// ResNetV2 50 model.
+// [input]
+// |
+// [globalAveragePool]
+// |
+// [conv2d]
+// |
+// [reshape]
+// |
+// [output]
+promise_test(async t => {
+ const avgPool2dInputShape = [1, 7, 7, 2048];
+ const avgPool2dInput = builder.input(
+ `avgPool2dInput`, {dataType: 'float32', dimensions: avgPool2dInputShape});
+ const avgPool2dOutput =
+ builder.averagePool2d(avgPool2dInput, {layout: 'nhwc'});
+ const conv2dFilterShape = [1001, 1, 1, 2048];
+ const conv2dFilter = builder.constant(
+ {dataType: 'float32', dimensions: conv2dFilterShape},
+ new Float32Array(sizeOfShape(conv2dFilterShape)).fill(1));
+ const conv2dBias = builder.constant(
+ {dataType: 'float32', dimensions: [1001]},
+ new Float32Array(1001).fill(0.01));
+ const conv2dOutput = builder.conv2d(avgPool2dOutput, conv2dFilter, {
+ inputLayout: 'nhwc',
+ filterLayout: 'ohwi',
+ padding: [0, 0, 0, 0],
+ bias: conv2dBias
+ });
+ const newShape = [1, 1001];
+ const reshapeOutput = builder.reshape(conv2dOutput, newShape);
+ assert_equals(reshapeOutput.dataType(), avgPool2dInput.dataType());
+ assert_array_equals(reshapeOutput.shape(), newShape);
+ const graph = await builder.build({reshapeOutput});
+ const result = await context.compute(
+ graph, {
+ 'avgPool2dInput':
+ new Float32Array(sizeOfShape(avgPool2dInputShape)).fill(0.1)
+ },
+ {'reshapeOutput': new Float32Array(1001)});
+}, 'Test global average pool operator\'s output shape for ResNetV2 50 model.');
+
+// This is used to reproduce an issue(crbug.com/331841268) of reduceMean in
+// ResNetV2 50 model.
+// [input]
+// |
+// [reduceMean]
+// |
+// [conv2d]
+// |
+// [reshape]
+// |
+// [output]
+promise_test(async t => {
+ const reduceMeanInputShape = [1, 7, 7, 2048];
+ const reduceMeanInput = builder.input(
+ `reduceMeanInput`,
+ {dataType: 'float32', dimensions: reduceMeanInputShape});
+ const reduceMeanOutput =
+ builder.reduceMean(reduceMeanInput, {axes: [1, 2], keepDimensions: true});
+ const conv2dFilterShape = [1001, 1, 1, 2048];
+ const conv2dFilter = builder.constant(
+ {dataType: 'float32', dimensions: conv2dFilterShape},
+ new Float32Array(sizeOfShape(conv2dFilterShape)).fill(1));
+ const conv2dBias = builder.constant(
+ {dataType: 'float32', dimensions: [1001]},
+ new Float32Array(1001).fill(0.01));
+ const conv2dOutput = builder.conv2d(reduceMeanOutput, conv2dFilter, {
+ inputLayout: 'nhwc',
+ filterLayout: 'ohwi',
+ padding: [0, 0, 0, 0],
+ bias: conv2dBias
+ });
+ const newShape = [1, 1001];
+ const reshapeOutput = builder.reshape(conv2dOutput, newShape);
+ assert_equals(reshapeOutput.dataType(), reduceMeanInput.dataType());
+ assert_array_equals(reshapeOutput.shape(), newShape);
+ const graph = await builder.build({reshapeOutput});
+ const result = await context.compute(
+ graph, {
+ 'reduceMeanInput':
+ new Float32Array(sizeOfShape(reduceMeanInputShape)).fill(0.1)
+ },
+ {'reshapeOutput': new Float32Array(1001)});
+}, 'Test reduceMean operator\'s output shape for ResNetV2 50 model.');
diff --git a/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js b/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js
index 435551b716..67491fbc16 100644
--- a/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js
@@ -12,3 +12,68 @@ multi_builder_test(async (t, builder, otherBuilder) => {
assert_throws_js(
TypeError, () => builder.reshape(inputFromOtherBuilder, newShape));
}, '[reshape] throw if input is from another builder');
+
+const tests = [
+ {
+ name: '[reshape] Test with new shape=[3, 8].',
+ input: {dataType: 'float32', dimensions: [2, 3, 4]},
+ newShape: [3, 8],
+ output: {dataType: 'float32', dimensions: [3, 8]}
+ },
+ {
+ name: '[reshape] Test with new shape=[24], src shape=[2, 3, 4].',
+ input: {dataType: 'float32', dimensions: [2, 3, 4]},
+ newShape: [24],
+ output: {dataType: 'float32', dimensions: [24]}
+ },
+ {
+ name: '[reshape] Test with new shape=[1], src shape=[1].',
+ input: {dataType: 'float32', dimensions: [1]},
+ newShape: [1],
+ output: {dataType: 'float32', dimensions: [1]}
+ },
+ {
+ name: '[reshape] Test reshaping a 1-D 1-element tensor to scalar.',
+ input: {dataType: 'float32', dimensions: [1]},
+ newShape: [],
+ output: {dataType: 'float32', dimensions: []}
+ },
+ {
+ name: '[reshape] Test reshaping a scalar to 1-D 1-element tensor.',
+ input: {dataType: 'float32', dimensions: []},
+ newShape: [1],
+ output: {dataType: 'float32', dimensions: [1]}
+ },
+ {
+ name: '[reshape] Throw if one value of new shape is 0.',
+ input: {dataType: 'float32', dimensions: [2, 4]},
+ newShape: [2, 4, 0],
+ },
+ {
+ name:
+ '[reshape] Throw if the number of elements implied by new shape is not equal to the number of elements in the input tensor when new shape=[].',
+ input: {dataType: 'float32', dimensions: [2, 3, 4]},
+ newShape: [],
+ },
+ {
+ name:
+ '[reshape] Throw if the number of elements implied by new shape is not equal to the number of elements in the input tensor.',
+ input: {dataType: 'float32', dimensions: [2, 3, 4]},
+ newShape: [3, 9],
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ if (test.output) {
+ const output = builder.reshape(input, test.newShape);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.reshape(input, test.newShape));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js b/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js
index a45ecd3fcb..de42621610 100644
--- a/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js
@@ -13,3 +13,69 @@ multi_builder_test(async (t, builder, otherBuilder) => {
assert_throws_js(
TypeError, () => builder.slice(inputFromOtherBuilder, starts, sizes));
}, '[slice] throw if input is from another builder');
+
+const tests = [
+ {
+ name: '[slice] Test with starts=[0, 1, 2] and sizes=[1, 2, 3].',
+ input: {dataType: 'float32', dimensions: [3, 4, 5]},
+ starts: [0, 1, 2],
+ sizes: [1, 2, 3],
+ output: {dataType: 'float32', dimensions: [1, 2, 3]}
+ },
+ {
+ name: '[slice] Throw if input is a scalar.',
+ input: {dataType: 'float32', dimensions: []},
+ starts: [0],
+ sizes: [1]
+ },
+ {
+ name:
+ '[slice] Throw if the length of sizes is not equal to the rank of the input tensor.',
+ input: {dataType: 'float32', dimensions: [3, 4, 5]},
+ starts: [1, 2, 3],
+ sizes: [1, 1]
+ },
+ {
+ name:
+ '[slice] Throw if the length of starts is not equal to the rank of the input tensor.',
+ input: {dataType: 'float32', dimensions: [3, 4, 5]},
+ starts: [1, 2, 1, 3],
+ sizes: [1, 1, 1]
+ },
+ {
+ name:
+ '[slice] Throw if the starting index is equal to or greater than input size in the same dimension.',
+ input: {dataType: 'float32', dimensions: [3, 4, 5]},
+ starts: [0, 4, 4],
+ sizes: [1, 1, 1]
+ },
+ {
+ name: '[slice] Throw if the number of elements to slice is equal to 0.',
+ input: {dataType: 'float32', dimensions: [3, 4, 5]},
+ starts: [1, 2, 3],
+ sizes: [1, 0, 1]
+ },
+ {
+ name:
+ '[slice] Throw if the ending index to slice is greater than input size in the same dimension.',
+ input: {dataType: 'float32', dimensions: [3, 4, 5]},
+ starts: [0, 1, 2],
+ sizes: [3, 4, 1]
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+
+ if (test.output) {
+ const output = builder.slice(input, test.starts, test.sizes);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.slice(input, test.starts, test.sizes));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js b/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js
index 347dfcd938..3cf91d26ec 100644
--- a/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js
@@ -5,3 +5,6 @@
'use strict';
validateInputFromAnotherBuilder('softplus');
+
+validateUnaryOperation(
+ 'softplus', floatingPointTypes, /*alsoBuildActivation=*/ true);
diff --git a/testing/web-platform/tests/webnn/validation_tests/split.https.any.js b/testing/web-platform/tests/webnn/validation_tests/split.https.any.js
index 38f3126603..6f7809744a 100644
--- a/testing/web-platform/tests/webnn/validation_tests/split.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/split.https.any.js
@@ -12,3 +12,83 @@ multi_builder_test(async (t, builder, otherBuilder) => {
assert_throws_js(
TypeError, () => builder.split(inputFromOtherBuilder, splits));
}, '[split] throw if input is from another builder');
+
+const tests = [
+ {
+ name: '[split] Test with default options.',
+ input: {dataType: 'float32', dimensions: [2, 6]},
+ splits: [2],
+ outputs: [
+ {dataType: 'float32', dimensions: [2, 6]},
+ ]
+ },
+ {
+ name:
+ '[split] Test with a sequence of unsigned long splits and with options.axis = 1.',
+ input: {dataType: 'float32', dimensions: [2, 6]},
+ splits: [1, 2, 3],
+ options: {axis: 1},
+ outputs: [
+ {dataType: 'float32', dimensions: [2, 1]},
+ {dataType: 'float32', dimensions: [2, 2]},
+ {dataType: 'float32', dimensions: [2, 3]},
+ ]
+ },
+ {
+ name: '[split] Throw if splitting a scalar.',
+ input: {dataType: 'float32', dimensions: []},
+ splits: [2],
+ },
+ {
+ name: '[split] Throw if axis is larger than input rank.',
+ input: {dataType: 'float32', dimensions: [2, 6]},
+ splits: [2],
+ options: {
+ axis: 2,
+ }
+ },
+ {
+ name: '[split] Throw if splits is equal to 0.',
+ input: {dataType: 'float32', dimensions: [2, 6]},
+ splits: [0],
+ options: {
+ axis: 2,
+ }
+ },
+ {
+ name:
+ '[split] Throw if the splits can not evenly divide the dimension size of input along options.axis.',
+ input: {dataType: 'float32', dimensions: [2, 5]},
+ splits: [2],
+ options: {
+ axis: 1,
+ }
+ },
+ {
+ name:
+ '[split] Throw if the sum of splits sizes not equal to the dimension size of input along options.axis.',
+ input: {dataType: 'float32', dimensions: [2, 6]},
+ splits: [2, 2, 3],
+ options: {
+ axis: 1,
+ }
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ if (test.outputs) {
+ const outputs = builder.split(input, test.splits, test.options);
+ assert_equals(outputs.length, test.outputs.length);
+ for (let i = 0; i < outputs.length; ++i) {
+ assert_equals(outputs[i].dataType(), test.outputs[i].dataType);
+ assert_array_equals(outputs[i].shape(), test.outputs[i].dimensions);
+ }
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.split(input, test.splits, test.options));
+ }
+ }, test.name));
diff --git a/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js b/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js
index 9ea5a5dcf8..3475a427d7 100644
--- a/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js
+++ b/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js
@@ -5,3 +5,54 @@
'use strict';
validateInputFromAnotherBuilder('transpose');
+
+const tests = [
+ {
+ name: '[transpose] Test building transpose with default options.',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ output: {dataType: 'float32', dimensions: [4, 3, 2, 1]}
+ },
+ {
+ name: '[transpose] Test building transpose with permutation=[0, 2, 3, 1].',
+ input: {dataType: 'float32', dimensions: [1, 2, 3, 4]},
+ options: {permutation: [0, 2, 3, 1]},
+ output: {dataType: 'float32', dimensions: [1, 3, 4, 2]}
+ },
+ {
+ name:
+ '[transpose] Throw if permutation\'s size is not the same as input\'s rank.',
+ input: {dataType: 'int32', dimensions: [1, 2, 4]},
+ options: {permutation: [0, 2, 3, 1]},
+ },
+ {
+ name: '[transpose] Throw if two values in permutation are same.',
+ input: {dataType: 'int32', dimensions: [1, 2, 3, 4]},
+ options: {permutation: [0, 2, 3, 2]},
+ },
+ {
+ name:
+ '[transpose] Throw if any value in permutation is not in the range [0,input\'s rank).',
+ input: {dataType: 'int32', dimensions: [1, 2, 3, 4]},
+ options: {permutation: [0, 1, 2, 4]},
+ },
+ {
+ name: '[transpose] Throw if any value in permutation is negative.',
+ input: {dataType: 'int32', dimensions: [1, 2, 3, 4]},
+ options: {permutation: [0, -1, 2, 3]},
+ }
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ if (test.output) {
+ const output = builder.transpose(input, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder.transpose(input, test.options));
+ }
+ }, test.name));