chromium/third_party/dawn/src/dawn/tests/unittests/wire/WireErrorCallbackTests.cpp

// Copyright 2019 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <memory>
#include <utility>

#include "dawn/common/FutureUtils.h"
#include "dawn/tests/unittests/wire/WireFutureTest.h"
#include "dawn/tests/unittests/wire/WireTest.h"
#include "dawn/wire/WireClient.h"

namespace dawn::wire {
namespace {

using testing::_;
using testing::DoAll;
using testing::InvokeWithoutArgs;
using testing::Mock;
using testing::Return;
using testing::SaveArg;
using testing::StrEq;
using testing::StrictMock;

// Mock classes to add expectations on the wire calling callbacks
class MockDeviceErrorCallback {};

std::unique_ptr<StrictMock<MockDeviceErrorCallback>> mockDeviceErrorCallback;
void ToMockDeviceErrorCallback(WGPUErrorType type, const char* message, void* userdata) {}

class MockDeviceLoggingCallback {};

std::unique_ptr<StrictMock<MockDeviceLoggingCallback>> mockDeviceLoggingCallback;
void ToMockDeviceLoggingCallback(WGPULoggingType type, const char* message, void* userdata) {}

class MockDeviceLostCallback {};

std::unique_ptr<StrictMock<MockDeviceLostCallback>> mockDeviceLostCallback;
void ToMockDeviceLostCallback(WGPUDeviceLostReason reason, const char* message, void* userdata) {}

class WireErrorCallbackTests : public WireTest {};

// Test the return wire for device validation error callbacks
TEST_F(WireErrorCallbackTests, DeviceValidationErrorCallback) {}

// Test the return wire for device OOM error callbacks
TEST_F(WireErrorCallbackTests, DeviceOutOfMemoryErrorCallback) {}

// Test the return wire for device internal error callbacks
TEST_F(WireErrorCallbackTests, DeviceInternalErrorCallback) {}

// Test the return wire for device user warning callbacks
TEST_F(WireErrorCallbackTests, DeviceLoggingCallback) {}

// Test the return wire for device lost callback
TEST_F(WireErrorCallbackTests, DeviceLostCallback) {}

class WirePopErrorScopeCallbackTests : public WireFutureTestWithParamsBase<> {};
DAWN_INSTANTIATE_WIRE_FUTURE_TEST_P(WirePopErrorScopeCallbackTests);

// Test the return wire for validation error scopes.
TEST_P(WirePopErrorScopeCallbackTests, TypeAndFilters) {
    static constexpr std::array<std::pair<WGPUErrorType, wgpu::ErrorFilter>, 3>
        kErrorTypeAndFilters = {{{WGPUErrorType_Validation, wgpu::ErrorFilter::Validation},
                                 {WGPUErrorType_OutOfMemory, wgpu::ErrorFilter::OutOfMemory},
                                 {WGPUErrorType_Internal, wgpu::ErrorFilter::Internal}}};

    for (const auto& [type, filter] : kErrorTypeAndFilters) {
        PushErrorScope(filter);

        DevicePopErrorScope(device, this);
        EXPECT_CALL(api, OnDevicePopErrorScope2(apiDevice, _)).WillOnce([&] {
            api.CallDevicePopErrorScope2Callback(apiDevice, WGPUPopErrorScopeStatus_Success, type,
                                                 "Some error message");
        });

        FlushClient();
        FlushFutures();
        ExpectWireCallbacksWhen(
            [&](auto& oldMockCb) {
                EXPECT_CALL(oldMockCb, Call(type, StrEq("Some error message"), this)).Times(1);

                FlushCallbacks();
            },
            [&](auto& mockCb) {
                EXPECT_CALL(mockCb, Call(WGPUPopErrorScopeStatus_Success, type,
                                         StrEq("Some error message"), this))
                    .Times(1);

                FlushCallbacks();
            });
    }
}

// Wire disconnect before server response calls the callback with Unknown error type.
// TODO(crbug.com/dawn/2021) When using new callback signature, check for InstanceDropped status.
TEST_P(WirePopErrorScopeCallbackTests, DisconnectBeforeServerReply) {
    PushErrorScope(wgpu::ErrorFilter::Validation);

    DevicePopErrorScope(device, this);
    EXPECT_CALL(api, OnDevicePopErrorScope2(apiDevice, _)).Times(1);

    FlushClient();
    FlushFutures();
    ExpectWireCallbacksWhen(
        [&](auto& oldMockCb) {
            EXPECT_CALL(oldMockCb, Call(WGPUErrorType_Unknown, nullptr, this)).Times(1);

            GetWireClient()->Disconnect();
        },
        [&](auto& mockCb) {
            EXPECT_CALL(mockCb, Call(WGPUPopErrorScopeStatus_InstanceDropped, WGPUErrorType_Unknown,
                                     nullptr, this))
                .Times(1);

            GetWireClient()->Disconnect();
        });
}

// Wire disconnect after server response calls the callback with returned error type.
TEST_P(WirePopErrorScopeCallbackTests, DisconnectAfterServerReply) {
    // On Async and Spontaneous mode, it is not possible to simulate this because on the server
    // reponse, the callback would also be fired.
    DAWN_SKIP_TEST_IF(IsSpontaneous());

    PushErrorScope(wgpu::ErrorFilter::Validation);

    DevicePopErrorScope(device, this);
    EXPECT_CALL(api, OnDevicePopErrorScope2(apiDevice, _)).WillOnce(InvokeWithoutArgs([&] {
        api.CallDevicePopErrorScope2Callback(apiDevice, WGPUPopErrorScopeStatus_Success,
                                             WGPUErrorType_Validation, "Some error message");
    }));

    FlushClient();
    FlushFutures();
    ExpectWireCallbacksWhen(
        [&](auto& oldMockCb) {
            EXPECT_CALL(oldMockCb, Call(WGPUErrorType_Validation, nullptr, this)).Times(1);

            GetWireClient()->Disconnect();
        },
        [&](auto& mockCb) {
            EXPECT_CALL(mockCb, Call(WGPUPopErrorScopeStatus_InstanceDropped,
                                     WGPUErrorType_Validation, nullptr, this))
                .Times(1);

            GetWireClient()->Disconnect();
        });
}

// Empty stack (We are emulating the errors that would be callback-ed from native).
TEST_P(WirePopErrorScopeCallbackTests, EmptyStack) {
    DevicePopErrorScope(cDevice, this);
    EXPECT_CALL(api, OnDevicePopErrorScope2(apiDevice, _)).WillOnce(InvokeWithoutArgs([&] {
        api.CallDevicePopErrorScope2Callback(apiDevice, WGPUPopErrorScopeStatus_Success,
                                             WGPUErrorType_Validation, "No error scopes to pop");
    }));

    FlushClient();
    FlushFutures();
    ExpectWireCallbacksWhen(
        [&](auto& oldMockCb) {
            EXPECT_CALL(oldMockCb,
                        Call(WGPUErrorType_Validation, StrEq("No error scopes to pop"), this))
                .Times(1);

            FlushCallbacks();
        },
        [&](auto& mockCb) {
            EXPECT_CALL(mockCb, Call(WGPUPopErrorScopeStatus_Success, WGPUErrorType_Validation,
                                     StrEq("No error scopes to pop"), this))
                .Times(1);

            FlushCallbacks();
        });
}

}  // anonymous namespace
}  // namespace dawn::wire