Skip to content

Commit 84eb4f6

Browse files
authored
Address resize shortcomings (microsoft#28779)
This pull request strengthens input validation and error handling for the ONNX `Resize` and `Upsample` operators, particularly around the `axes`, `scales`, and `roi` attributes. It ensures compliance with the ONNX specification and prevents invalid or ambiguous input from causing incorrect behavior or crashes. The changes also introduce comprehensive unit tests to verify these new validation paths. **Key improvements include:** ### Validation and Error Handling - **Axes Attribute Validation:** The `axes` attribute is validated after negative-axis normalization once the input rank is known — at construction when the rank is statically available, otherwise on the first `Compute()`. This rejects duplicates that collide only after canonicalization (e.g. `{-1, rank-1}`) as well as out-of-range entries, with clear error messages. The normalized axes are cached to keep the inference hot path allocation-free. - **Scales Validation:** All scale values are checked for finiteness (no `NaN` or `Inf` allowed), and for non-Resize modes, scale values must be ≥ 1. This prevents invalid scaling operations. ### ROI and Sizes Validation - **ROI Length Check:** When `axes` are provided, the `roi` input length must be either `2 * len(axes)` (per-axis ROI) or `2 * rank` (default ROI). Anything else is rejected. The default-ROI path no longer scatters spurious zeros into the canonical `[0..0, 1..1]` buffer when `axes` is partial. - **Sizes/Axes Count Consistency:** If `axes` are given, the number of elements in the `sizes` input must match the number of axes, ensuring correct output shape computation. ### Error Propagation and Refactoring - **Status Return for ROI Calculation:** The `ComputeROIWithAxes` function now returns a `Status` object, and all its callers (CPU, CUDA, WebGPU EPs) are updated to propagate errors instead of assuming success. ### Testing - **Comprehensive Unit Tests:** New tests cover negative and out-of-range axes, post-normalization duplicate axes (including `{-1, rank-1}`), mismatched sizes/axes counts, non-finite scale values, and invalid ROI length. These tests ensure that invalid input is consistently rejected across all relevant code paths and work correctly in builds without exceptions. ### Miscellaneous - **Test File Improvements:** The test file now includes `<limits>` to support testing of `NaN` and `Inf` values in scales. These changes collectively make the `Resize` and `Upsample` operators more robust and standards-compliant, and help prevent subtle bugs due to invalid input.
1 parent 4eb757c commit 84eb4f6

5 files changed

Lines changed: 281 additions & 20 deletions

File tree

onnxruntime/core/providers/cpu/tensor/upsample.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1398,7 +1398,7 @@ Status Upsample<T>::Compute(OpKernelContext* context) const {
13981398
}
13991399
}
14001400

1401-
ComputeROIWithAxes(roi_array, input_dims.size());
1401+
ORT_RETURN_IF_ERROR(ComputeROIWithAxes(roi_array, input_dims.size()));
14021402
// Get scales data
14031403
InlinedVector<float> scales_array(input_dims.size());
14041404

onnxruntime/core/providers/cpu/tensor/upsamplebase.h

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ class UpsampleBase {
233233
// guard against unit tests that can add an attribute
234234
auto axes = info.template GetAttrsOrDefault<int64_t>("axes");
235235
axes_.assign(axes.cbegin(), axes.cend());
236+
// Uniqueness of axes is enforced after negative-axis normalization in
237+
// ValidateAndNormalizeAxes, so two raw entries that collide after canonicalization
238+
// (e.g. {-1, rank-1}) are still rejected.
236239
}
237240

238241
extrapolation_value_ = info.template GetAttrOrDefault<float>("extrapolation_value", 0.0f);
@@ -301,6 +304,12 @@ class UpsampleBase {
301304
auto tensor_info = type_info.GetTensorTypeAndShapeInfo();
302305
rank = static_cast<int64_t>(tensor_info.GetDimensionsCount());
303306
}
307+
// When axes are present and the input rank is statically known, validate axes and cache
308+
// the normalized form so the inference hot path can skip the bitmap allocation.
309+
if (rank > 0 && !axes_.empty()) {
310+
ORT_THROW_IF_ERROR(ValidateAndNormalizeAxes(rank, normalized_axes_));
311+
normalized_axes_rank_ = rank;
312+
}
304313
if (get_scale && scale->Shape().Size() > 0 && ((opset < 18) || (rank > 0 && opset >= 18))) {
305314
ORT_THROW_IF_ERROR(ParseScalesData(scale, scales_, rank));
306315
scales_cached_ = true;
@@ -335,6 +344,8 @@ class UpsampleBase {
335344
InlinedVector<float> scales_;
336345
InlinedVector<float> roi_;
337346
TensorShapeVector axes_;
347+
TensorShapeVector normalized_axes_;
348+
int64_t normalized_axes_rank_ = -1;
338349

339350
bool scales_cached_;
340351
bool roi_cached_;
@@ -497,7 +508,37 @@ class UpsampleBase {
497508
}
498509
}
499510

511+
// Resolve negative entries in axes_ against the supplied rank, verify each is in range,
512+
// and verify that no two entries collide after normalization. Populates normalized_axes
513+
// with the canonical non-negative indices in the original order. When the input rank is
514+
// statically known, the constructor pre-populates normalized_axes_ and this call becomes
515+
// a copy on the inference hot path.
516+
Status ValidateAndNormalizeAxes(int64_t rank, TensorShapeVector& normalized_axes) const {
517+
ORT_RETURN_IF_NOT(rank > 0, "Rank must be positive when axes is provided.");
518+
if (rank == normalized_axes_rank_) {
519+
normalized_axes = normalized_axes_;
520+
return Status::OK();
521+
}
522+
normalized_axes.clear();
523+
normalized_axes.reserve(axes_.size());
524+
InlinedVector<bool> seen(static_cast<size_t>(rank), false);
525+
for (int64_t raw_axis : axes_) {
526+
ORT_RETURN_IF_NOT(IsAxisInRange(raw_axis, rank), "axis ", raw_axis,
527+
" is not in valid range [-", rank, ",", rank - 1, "]");
528+
const int64_t axis = raw_axis < 0 ? raw_axis + rank : raw_axis;
529+
const auto idx = static_cast<size_t>(axis);
530+
ORT_RETURN_IF(seen[idx], "axes attribute contains duplicate axis ", axis,
531+
" after negative-axis normalization (rank=", rank, ").");
532+
seen[idx] = true;
533+
normalized_axes.push_back(axis);
534+
}
535+
return Status::OK();
536+
}
537+
500538
[[nodiscard]] Status ScalesValidation(gsl::span<const float> scales, const UpsampleMode mode) const {
539+
for (auto& scale : scales) {
540+
ORT_RETURN_IF_NOT(std::isfinite(scale), "Scale value must be finite.");
541+
}
501542
if (!is_resize_) {
502543
for (auto& scale : scales) {
503544
ORT_RETURN_IF_NOT(scale >= 1, "Scale value should be greater than or equal to 1.");
@@ -571,9 +612,10 @@ class UpsampleBase {
571612
"Number of elements in scales should be equal to number of axes.");
572613

573614
InlinedVector<float> new_scales(size_t(rank), 1.0f);
574-
for (size_t i = 0; i < axes_.size(); i++) {
575-
const int64_t axis = HandleNegativeAxis(axes_[i], rank);
576-
new_scales[static_cast<size_t>(axis)] = scales[i];
615+
TensorShapeVector normalized_axes;
616+
ORT_RETURN_IF_ERROR(ValidateAndNormalizeAxes(rank, normalized_axes));
617+
for (size_t i = 0; i < normalized_axes.size(); i++) {
618+
new_scales[static_cast<size_t>(normalized_axes[i])] = scales[i];
577619
}
578620
scales.swap(new_scales);
579621
}
@@ -598,11 +640,14 @@ class UpsampleBase {
598640
"Resize: input tensor's rank does not match the output tensor's rank.");
599641

600642
if (axes_.size()) {
643+
ORT_RETURN_IF_NOT(axes_.size() == size_span.size(),
644+
"Number of elements in sizes should be equal to number of axes.");
601645
output_dims.assign(input_dims.begin(), input_dims.end());
602646
const int64_t rank = static_cast<int64_t>(output_dims.size());
603-
for (size_t i = 0; i < axes_.size(); i++) {
604-
const int64_t axis = HandleNegativeAxis(axes_[i], rank);
605-
output_dims[static_cast<size_t>(axis)] = size_span[i];
647+
TensorShapeVector normalized_axes;
648+
ORT_RETURN_IF_ERROR(ValidateAndNormalizeAxes(rank, normalized_axes));
649+
for (size_t i = 0; i < normalized_axes.size(); i++) {
650+
output_dims[static_cast<size_t>(normalized_axes[i])] = size_span[i];
606651
}
607652
} else {
608653
std::copy(size_span.begin(), size_span.end(), output_dims.begin());
@@ -651,21 +696,33 @@ class UpsampleBase {
651696

652697
// Roi is redefined in Opset-18, we have a concept of axes.
653698
// So we need to update it accordingly.
654-
void ComputeROIWithAxes(InlinedVector<float>& roi_array, size_t rank) const {
699+
Status ComputeROIWithAxes(InlinedVector<float>& roi_array, size_t rank) const {
655700
if (axes_.size()) {
656-
InlinedVector<float> roi_tmp(rank * 2, 0);
657-
for (size_t i = rank; i < rank * 2; ++i) {
658-
roi_tmp[i] = 1;
659-
}
701+
// Per-axis ROI is supplied as a flat [start..., end...] of length 2 * len(axes).
702+
// The default-ROI path fills roi_array with [0..., 1...] of length 2 * rank, in which
703+
// case the canonical full-rank ROI is already correct and no scatter is needed.
704+
const bool per_axis_roi = roi_array.size() == 2 * axes_.size();
705+
ORT_RETURN_IF_NOT(per_axis_roi || roi_array.size() == 2 * rank,
706+
"roi input length (", roi_array.size(),
707+
") must be either 2 * rank (", 2 * rank,
708+
") or 2 * number of axes (", 2 * axes_.size(), ").");
660709
const int64_t rank_i64 = static_cast<int64_t>(rank);
661-
for (size_t i = 0; i < axes_.size(); i++) {
662-
const int64_t axis = HandleNegativeAxis(axes_[i], rank_i64);
663-
auto v_in_axes = static_cast<size_t>(axis);
664-
roi_tmp[v_in_axes] = (roi_array[i]);
665-
roi_tmp[rank + v_in_axes] = (roi_array[axes_.size() + i]);
710+
TensorShapeVector normalized_axes;
711+
ORT_RETURN_IF_ERROR(ValidateAndNormalizeAxes(rank_i64, normalized_axes));
712+
if (per_axis_roi) {
713+
InlinedVector<float> roi_tmp(rank * 2, 0);
714+
for (size_t i = rank; i < rank * 2; ++i) {
715+
roi_tmp[i] = 1;
716+
}
717+
for (size_t i = 0; i < normalized_axes.size(); i++) {
718+
const auto v_in_axes = static_cast<size_t>(normalized_axes[i]);
719+
roi_tmp[v_in_axes] = roi_array[i];
720+
roi_tmp[rank + v_in_axes] = roi_array[axes_.size() + i];
721+
}
722+
roi_array.swap(roi_tmp);
666723
}
667-
roi_array.swap(roi_tmp);
668724
}
725+
return Status::OK();
669726
}
670727

671728
public:

onnxruntime/core/providers/cuda/tensor/upsample.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ Status Upsample<T>::ComputeInternal(OpKernelContext* context) const {
426426
}
427427
}
428428

429-
ComputeROIWithAxes(roi_array, input_dims.size());
429+
ORT_RETURN_IF_ERROR(ComputeROIWithAxes(roi_array, input_dims.size()));
430430

431431
InlinedVector<float> scales_array(input_dims.size());
432432
// opset < 10

onnxruntime/core/providers/webgpu/tensor/upsample.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ Status Upsample::ComputeInternal(ComputeContext& context) const {
8686
}
8787
}
8888

89-
ComputeROIWithAxes(roi_array, input_dims.size());
89+
ORT_RETURN_IF_ERROR(ComputeROIWithAxes(roi_array, input_dims.size()));
9090

9191
InlinedVector<float> scales_array(input_dims.size());
9292
// opset < 10

onnxruntime/test/providers/cpu/tensor/resize_op_test.cc

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
#include <exception>
5+
#include <limits>
56
#include "gtest/gtest.h"
67
#include "test/providers/provider_test_utils.h"
78
#include "test/util/include/default_providers.h"
@@ -3187,6 +3188,209 @@ TEST(ResizeOpTest, Axes_OutOfRange_18) {
31873188
{kTensorrtExecutionProvider, kQnnExecutionProvider, kDmlExecutionProvider});
31883189
}
31893190

3191+
// Negative axis below the valid range must be rejected before being used as a scatter index.
3192+
TEST(ResizeOpTest, Axes_NegativeOutOfRange_18) {
3193+
std::vector<float> X(16 * 4);
3194+
std::iota(X.begin(), X.end(), 0.f);
3195+
std::vector<float> roi{};
3196+
std::vector<float> scales{0.75f, 0.75f, 0.75f};
3197+
std::vector<int64_t> axes{2, 3, -6};
3198+
std::vector<float> Y(16 * 4, 0.0f);
3199+
3200+
OpTester test("Resize", 18);
3201+
test.AddShapeToTensorData(false);
3202+
test.AddAttribute("mode", "linear");
3203+
test.AddAttribute<std::vector<int64_t>>("axes", axes);
3204+
3205+
test.AddInput<float>("X", {1, 1, 4, 4, 4}, X);
3206+
test.AddInput<float>("roi", {0}, roi);
3207+
test.AddInput<float>("scales", {int64_t(scales.size())}, scales);
3208+
test.AddOutput<float>("Y", {1, 1, 4, 4, 4}, Y);
3209+
3210+
// TensorRT, QNN, and DML do not exercise the CPU axes-validation path.
3211+
test.Run(OpTester::ExpectResult::kExpectFailure,
3212+
"axis -6 is not in valid range [-5,4]",
3213+
{kTensorrtExecutionProvider, kQnnExecutionProvider, kDmlExecutionProvider});
3214+
}
3215+
3216+
// Valid negative axes (within [-rank, -1]) must still produce correct output.
3217+
TEST(ResizeOpTest, Axes_NegativeInRange_18) {
3218+
std::vector<float> X(16 * 4);
3219+
std::iota(X.begin(), X.end(), 0.f);
3220+
std::vector<float> Y = {3.5f, 4.8333335f, 6.1666665f, 8.833333f, 10.166667f, 11.5f, 14.166667f,
3221+
15.5f, 16.833334f, 24.833334f, 26.166666f, 27.5f, 30.166666f, 31.5f,
3222+
32.833332f, 35.5f, 36.833332f, 38.166668f, 46.166668f, 47.5f, 48.833332f,
3223+
51.5f, 52.833332f, 54.166668f, 56.833332f, 58.166668f, 59.5f};
3224+
std::vector<float> roi{};
3225+
std::vector<float> scales{3 / 4.0f, 3 / 4.0f, 3 / 4.0f};
3226+
std::vector<int64_t> output_shape{1, 1, 3, 3, 3};
3227+
std::vector<int64_t> axes{-3, -2, -1};
3228+
3229+
OpTester test("Resize", 18);
3230+
test.AddAttribute<int64_t>("exclude_outside", 0LL);
3231+
test.AddAttribute<std::vector<int64_t>>("axes", axes);
3232+
test.AddAttribute<int64_t>("antialias", 0LL);
3233+
test.AddAttribute("mode", "linear");
3234+
3235+
test.AddInput<float>("X", {1, 1, 4, 4, 4}, X);
3236+
test.AddInput<float>("roi", {int64_t(roi.size())}, roi);
3237+
test.AddInput<float>("scales", {int64_t(scales.size())}, scales, true);
3238+
3239+
test.AddOutput<float>("Y", output_shape, Y);
3240+
// OpenVINO EP's Resize importer does not normalize negative axes against the input rank,
3241+
// so it rejects models that the ONNX spec accepts. Tracked by GH issue #28788.
3242+
test.Run(OpTester::ExpectResult::kExpectSuccess, "",
3243+
{kTensorrtExecutionProvider, kQnnExecutionProvider, kOpenVINOExecutionProvider});
3244+
}
3245+
3246+
// When axes is provided, the sizes input length must match axes length so the scatter
3247+
// loop does not read past the end of sizes.
3248+
TEST(ResizeOpTest, Axes_and_Sizes_CountMismatch_18) {
3249+
std::vector<float> X(16 * 4);
3250+
std::iota(X.begin(), X.end(), 0.f);
3251+
std::vector<float> roi{};
3252+
std::vector<float> scales{};
3253+
std::vector<int64_t> sizes{3, 3};
3254+
std::vector<int64_t> axes{2, 3, 4};
3255+
std::vector<float> Y(16 * 4, 0.0f);
3256+
3257+
OpTester test("Resize", 18);
3258+
test.AddShapeToTensorData(false);
3259+
test.AddAttribute("mode", "linear");
3260+
test.AddAttribute<std::vector<int64_t>>("axes", axes);
3261+
3262+
test.AddInput<float>("X", {1, 1, 4, 4, 4}, X);
3263+
test.AddInput<float>("roi", {0}, roi);
3264+
test.AddInput<float>("", {0}, scales);
3265+
test.AddInput<int64_t>("sizes", {int64_t(sizes.size())}, sizes);
3266+
test.AddOutput<float>("Y", {1, 1, 4, 4, 4}, Y);
3267+
3268+
test.Run(OpTester::ExpectResult::kExpectFailure,
3269+
"Number of elements in sizes should be equal to number of axes.",
3270+
{kTensorrtExecutionProvider, kQnnExecutionProvider, kDmlExecutionProvider});
3271+
}
3272+
3273+
// Non-finite scale values must be rejected before being multiplied into output dimensions.
3274+
TEST(ResizeOpTest, Scales_NaN_Rejected_18) {
3275+
std::vector<float> X(16, 1.0f);
3276+
std::vector<float> roi{};
3277+
std::vector<float> scales{1.0f, 1.0f, std::numeric_limits<float>::quiet_NaN(), 2.0f};
3278+
std::vector<float> Y(32, 0.0f);
3279+
3280+
OpTester test("Resize", 18);
3281+
test.AddShapeToTensorData(false);
3282+
test.AddAttribute("mode", "linear");
3283+
3284+
test.AddInput<float>("X", {1, 1, 4, 4}, X);
3285+
test.AddInput<float>("roi", {0}, roi);
3286+
test.AddInput<float>("scales", {int64_t(scales.size())}, scales);
3287+
test.AddOutput<float>("Y", {1, 1, 4, 8}, Y);
3288+
3289+
// EPs that do their own validation or do not exercise the CPU ScalesValidation path.
3290+
test.Run(OpTester::ExpectResult::kExpectFailure, "Scale value must be finite.",
3291+
{kTensorrtExecutionProvider, kQnnExecutionProvider, kDmlExecutionProvider,
3292+
kOpenVINOExecutionProvider});
3293+
}
3294+
3295+
TEST(ResizeOpTest, Scales_PositiveInf_Rejected_18) {
3296+
std::vector<float> X(16, 1.0f);
3297+
std::vector<float> roi{};
3298+
std::vector<float> scales{1.0f, 1.0f, 2.0f, std::numeric_limits<float>::infinity()};
3299+
std::vector<float> Y(32, 0.0f);
3300+
3301+
OpTester test("Resize", 18);
3302+
test.AddShapeToTensorData(false);
3303+
test.AddAttribute("mode", "linear");
3304+
3305+
test.AddInput<float>("X", {1, 1, 4, 4}, X);
3306+
test.AddInput<float>("roi", {0}, roi);
3307+
test.AddInput<float>("scales", {int64_t(scales.size())}, scales);
3308+
test.AddOutput<float>("Y", {1, 1, 8, 4}, Y);
3309+
3310+
test.Run(OpTester::ExpectResult::kExpectFailure, "Scale value must be finite.",
3311+
{kTensorrtExecutionProvider, kQnnExecutionProvider, kDmlExecutionProvider,
3312+
kOpenVINOExecutionProvider});
3313+
}
3314+
3315+
// Duplicate values in the axes attribute violate the ONNX spec. The check runs after
3316+
// negative-axis normalization, so it covers both raw duplicates and pairs that collide
3317+
// only after canonicalization (e.g. {-1, rank-1}).
3318+
TEST(ResizeOpTest, Axes_Duplicate_Rejected_18) {
3319+
std::vector<float> X(16 * 4);
3320+
std::iota(X.begin(), X.end(), 0.f);
3321+
std::vector<float> roi{};
3322+
std::vector<float> scales{0.75f, 0.75f, 0.75f};
3323+
std::vector<int64_t> axes{2, 3, 2};
3324+
std::vector<float> Y(16 * 4, 0.0f);
3325+
3326+
OpTester test("Resize", 18);
3327+
test.AddShapeToTensorData(false);
3328+
test.AddAttribute("mode", "linear");
3329+
test.AddAttribute<std::vector<int64_t>>("axes", axes);
3330+
3331+
test.AddInput<float>("X", {1, 1, 4, 4, 4}, X);
3332+
test.AddInput<float>("roi", {0}, roi);
3333+
test.AddInput<float>("scales", {int64_t(scales.size())}, scales);
3334+
test.AddOutput<float>("Y", {1, 1, 4, 4, 4}, Y);
3335+
3336+
test.Run(OpTester::ExpectResult::kExpectFailure,
3337+
"axes attribute contains duplicate axis 2",
3338+
{kTensorrtExecutionProvider, kQnnExecutionProvider, kDmlExecutionProvider});
3339+
}
3340+
3341+
// Negative and positive axis entries that resolve to the same canonical index must be
3342+
// rejected. For rank=5, axes={-1, 4} both map to 4.
3343+
TEST(ResizeOpTest, Axes_Duplicate_AfterNormalization_Rejected_18) {
3344+
std::vector<float> X(16 * 4);
3345+
std::iota(X.begin(), X.end(), 0.f);
3346+
std::vector<float> roi{};
3347+
std::vector<float> scales{0.75f, 0.75f};
3348+
std::vector<int64_t> axes{-1, 4};
3349+
std::vector<float> Y(16 * 4, 0.0f);
3350+
3351+
OpTester test("Resize", 18);
3352+
test.AddShapeToTensorData(false);
3353+
test.AddAttribute("mode", "linear");
3354+
test.AddAttribute<std::vector<int64_t>>("axes", axes);
3355+
3356+
test.AddInput<float>("X", {1, 1, 4, 4, 4}, X);
3357+
test.AddInput<float>("roi", {0}, roi);
3358+
test.AddInput<float>("scales", {int64_t(scales.size())}, scales);
3359+
test.AddOutput<float>("Y", {1, 1, 4, 4, 4}, Y);
3360+
3361+
test.Run(OpTester::ExpectResult::kExpectFailure,
3362+
"axes attribute contains duplicate axis 4",
3363+
{kTensorrtExecutionProvider, kQnnExecutionProvider, kDmlExecutionProvider});
3364+
}
3365+
3366+
// When axes is provided in tf_crop_and_resize mode, the roi input must contain at least
3367+
// 2 * len(axes) entries so the per-axis start/end pairs can be read safely.
3368+
TEST(ResizeOpTest, Roi_TooShortForAxes_18) {
3369+
std::vector<float> X(16 * 4);
3370+
std::iota(X.begin(), X.end(), 0.f);
3371+
// roi length 4 (= 2 * 2), but axes has 3 entries, so 2 * len(axes) = 6 are required.
3372+
std::vector<float> roi{0.0f, 0.0f, 1.0f, 1.0f};
3373+
std::vector<float> scales{0.75f, 0.75f, 0.75f};
3374+
std::vector<int64_t> axes{2, 3, 4};
3375+
std::vector<float> Y(16 * 4, 0.0f);
3376+
3377+
OpTester test("Resize", 18);
3378+
test.AddShapeToTensorData(false);
3379+
test.AddAttribute("mode", "linear");
3380+
test.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize");
3381+
test.AddAttribute<std::vector<int64_t>>("axes", axes);
3382+
3383+
test.AddInput<float>("X", {1, 1, 4, 4, 4}, X);
3384+
test.AddInput<float>("roi", {int64_t(roi.size())}, roi);
3385+
test.AddInput<float>("scales", {int64_t(scales.size())}, scales);
3386+
test.AddOutput<float>("Y", {1, 1, 4, 4, 4}, Y);
3387+
3388+
test.Run(OpTester::ExpectResult::kExpectFailure,
3389+
"roi input length",
3390+
{kTensorrtExecutionProvider, kQnnExecutionProvider, kDmlExecutionProvider,
3391+
kOpenVINOExecutionProvider});
3392+
}
3393+
31903394
TEST(ResizeOpTest, Sizes_RankMismatch_13) {
31913395
OpTester test("Resize", 13);
31923396
test.AddShapeToTensorData(false);

0 commit comments

Comments
 (0)