diff --git a/source/custom/visa_service.custom.cpp b/source/custom/visa_service.custom.cpp index c6854e375..2ff7fc92c 100644 --- a/source/custom/visa_service.custom.cpp +++ b/source/custom/visa_service.custom.cpp @@ -170,18 +170,27 @@ static ViStatus GetAttributeValue(ViObject vi, ViAttr attributeID, VisaService:: return status; } -::grpc::Status VisaService::ConvertApiErrorStatusForViSession(::grpc::ServerContextBase* context, int32_t status, ViSession vi) +::grpc::Status ConvertVisaApiErrorStatus(::grpc::ServerContextBase* context, ViStatus status, ViObject vi, VisaService::LibrarySharedPtr library, const char* extraInfo = nullptr) { std::string description(nidevice_grpc::kMaxGrpcErrorDescriptionSize, '\0'); - library_->StatusDesc(vi, status, &description[0]); + library->StatusDesc(vi, status, &description[0]); + // Although NI-VISA messages don't currently include context, we shouldn't assume it. + if (extraInfo != nullptr && description.find(extraInfo) == description.npos) { + nidevice_grpc::converters::trim_trailing_nulls(description); + description.append("\n"); + description.append(extraInfo); + } return nidevice_grpc::ApiErrorAndDescriptionToStatus(context, status, description); } +::grpc::Status VisaService::ConvertApiErrorStatusForViSession(::grpc::ServerContextBase* context, int32_t status, ViSession vi) +{ + return ConvertVisaApiErrorStatus(context, status, vi, library_); +} + ::grpc::Status VisaService::ConvertApiErrorStatusForViObject(::grpc::ServerContextBase* context, int32_t status, ViObject vi) { - std::string description(nidevice_grpc::kMaxGrpcErrorDescriptionSize, '\0'); - library_->StatusDesc(vi, status, &description[0]); - return nidevice_grpc::ApiErrorAndDescriptionToStatus(context, status, description); + return ConvertVisaApiErrorStatus(context, status, vi, library_); } @@ -335,7 +344,7 @@ ::grpc::Status VisaService::Open(::grpc::ServerContext* context, const OpenReque auto cleanup_lambda = [library](ViSession id) { library->Close(id); }; int status = session_repository_->add_session(grpc_device_session_name, init_lambda, cleanup_lambda, initialization_behavior, &new_session_initialized); if (!status_ok(status)) { - return ConvertApiErrorStatusForViSession(context, status, rsrc_manager_handle); + return ConvertVisaApiErrorStatus(context, status, rsrc_manager_handle, library_, instrument_descriptor); } ViUInt16 numEvents = 0; @@ -431,7 +440,7 @@ ::grpc::Status VisaService::ParseRsrc(::grpc::ServerContext* context, const Pars std::string alias_if_exists(256 - 1, '\0'); auto status = library_->ParseRsrc(rsrc_manager_handle, resource_name, &interface_type, &interface_number, (ViChar*)resource_class.data(), (ViChar*)expanded_unaliased_name.data(), (ViChar*)alias_if_exists.data()); if (!status_ok(status)) { - return ConvertApiErrorStatusForViSession(context, status, rsrc_manager_handle); + return ConvertVisaApiErrorStatus(context, status, rsrc_manager_handle, library_, resource_name); } response->set_status(status); response->set_interface_type(interface_type); diff --git a/source/tests/system/visa_driver_api_tests.cpp b/source/tests/system/visa_driver_api_tests.cpp index b2a2589bd..00b852200 100644 --- a/source/tests/system/visa_driver_api_tests.cpp +++ b/source/tests/system/visa_driver_api_tests.cpp @@ -58,8 +58,8 @@ class VisaDriverApiTest : public ::testing::Test { client::raise_if_error(status, context); driver_session_ = std::make_unique(response.vi()); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(VI_SUCCESS, response.status()); + ASSERT_TRUE(status.ok()); + ASSERT_EQ(VI_SUCCESS, response.status()); } void close_driver_session() @@ -164,7 +164,7 @@ class VisaMessageBasedLoopbackTest : public VisaDriverApiTest { void SetUp() override { - EXPECT_EQ(0, echoserver_.start()); + ASSERT_EQ(0, echoserver_.start()); std::string portNumber(std::to_string(echoserver_.get_server_port())); std::string instrument_descriptor("TCPIP0::localhost::" + portNumber + "::SOCKET"); @@ -175,6 +175,10 @@ class VisaMessageBasedLoopbackTest : public VisaDriverApiTest { void TearDown() override { + echoserver_.prepare_stop(); + // Sending any single byte at this point will stop the server loop. + client::write(GetStub(), GetNamedSession(), " "); + close_driver_session(); echoserver_.stop(); } @@ -207,7 +211,7 @@ class VisaMessageBasedLoopbackTest : public VisaDriverApiTest { void read(size_t count, const std::string& expectedData, ViStatus expectedStatus) { - auto response = client::read(GetStub(), GetNamedSession(), count); + auto response = client::read(GetStub(), GetNamedSession(), static_cast(count)); EXPECT_EQ(expectedStatus, response.status()); EXPECT_EQ(expectedData.size(), response.return_count()); EXPECT_EQ(expectedData, response.buffer()); @@ -215,7 +219,7 @@ class VisaMessageBasedLoopbackTest : public VisaDriverApiTest { void read_async(size_t count, const std::string& expectedData, ViStatus expectedStatus, bool requiresTerminate = false) { - auto asyncResponse = client::read_async(GetStub(), GetNamedSession(), count); + auto asyncResponse = client::read_async(GetStub(), GetNamedSession(), static_cast(count)); EXPECT_THAT((std::array{ VI_SUCCESS, VI_SUCCESS_SYNC }), testing::Contains(asyncResponse.status())); if (requiresTerminate) { std::this_thread::sleep_for(std::chrono::milliseconds(kFastTimeoutMsec)); @@ -374,7 +378,7 @@ TEST_F(VisaMessageBasedLoopbackTest, Flush_ClearsBuffer) class VisaRegisterBasedLoopbackTest : public VisaDriverApiTest { public: - VisaRegisterBasedLoopbackTest() : allocatedBase_(0) + VisaRegisterBasedLoopbackTest() : allocatedBase_(0), mappedBase_(0) { } virtual ~VisaRegisterBasedLoopbackTest() @@ -384,7 +388,9 @@ class VisaRegisterBasedLoopbackTest : public VisaDriverApiTest { void SetUp() override { initialize_driver_session("PXI::MEMACC"); - allocatedBase_ = allocate(1024); + auto response = client::mem_alloc_ex(GetStub(), GetNamedSession(), 4096); + ASSERT_EQ(VI_SUCCESS, response.status()); + allocatedBase_ = response.offset(); } void TearDown() override @@ -393,13 +399,6 @@ class VisaRegisterBasedLoopbackTest : public VisaDriverApiTest { close_driver_session(); } - uint64_t allocate(uint64_t size) - { - auto response = client::mem_alloc_ex(GetStub(), GetNamedSession(), size); - EXPECT_EQ(VI_SUCCESS, response.status()); - return response.offset(); - } - void free(uint64_t offset) { auto response = client::mem_free(GetStub(), GetNamedSession(), offset); @@ -528,8 +527,74 @@ class VisaRegisterBasedLoopbackTest : public VisaDriverApiTest { EXPECT_EQ(VI_SUCCESS, response.status()); } + void map() + { + auto response = client::map_address(GetStub(), GetNamedSession(), visa::ADDRESS_SPACE_PXI_ALLOC_SPACE, allocatedBase_, 4096, false, 0); + EXPECT_EQ(VI_SUCCESS, response.status()); + mappedBase_ = response.address(); + } + + void unmap() + { + auto response = client::unmap_address(GetStub(), GetNamedSession()); + EXPECT_EQ(VI_SUCCESS, response.status()); + } + + ViUInt8 peek8(uint64_t offset) + { + auto response = client::peek8(GetStub(), GetNamedSession(), mappedBase_ + offset); + EXPECT_EQ(VI_SUCCESS, response.status()); + return response.value(); + } + + ViUInt16 peek16(uint64_t offset) + { + auto response = client::peek16(GetStub(), GetNamedSession(), mappedBase_ + offset); + EXPECT_EQ(VI_SUCCESS, response.status()); + return response.value(); + } + + ViUInt32 peek32(uint64_t offset) + { + auto response = client::peek32(GetStub(), GetNamedSession(), mappedBase_ + offset); + EXPECT_EQ(VI_SUCCESS, response.status()); + return response.value(); + } + + ViUInt64 peek64(uint64_t offset) + { + auto response = client::peek64(GetStub(), GetNamedSession(), mappedBase_ + offset); + EXPECT_EQ(VI_SUCCESS, response.status()); + return response.value(); + } + + void poke8(uint64_t offset, ViUInt8 value) + { + auto response = client::poke8(GetStub(), GetNamedSession(), mappedBase_ + offset, value); + EXPECT_EQ(VI_SUCCESS, response.status()); + } + + void poke16(uint64_t offset, ViUInt16 value) + { + auto response = client::poke16(GetStub(), GetNamedSession(), mappedBase_ + offset, value); + EXPECT_EQ(VI_SUCCESS, response.status()); + } + + void poke32(uint64_t offset, ViUInt32 value) + { + auto response = client::poke32(GetStub(), GetNamedSession(), mappedBase_ + offset, value); + EXPECT_EQ(VI_SUCCESS, response.status()); + } + + void poke64(uint64_t offset, ViUInt64 value) + { + auto response = client::poke64(GetStub(), GetNamedSession(), mappedBase_ + offset, value); + EXPECT_EQ(VI_SUCCESS, response.status()); + } + private: uint64_t allocatedBase_; + uint64_t mappedBase_; }; TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithMoveOut8_In8_ReadsData) @@ -632,6 +697,122 @@ TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithOut64_MoveIn64_ReadsData) } } +TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithMoveOut8_Peek8_ReadsData) +{ + map(); + const int testLength = 5; + const int startingOffset = 11; + ViUInt8 buffer[testLength] = { 0x01, 0x02, 0x00, 0x03, 0xFE }; + moveOut8(startingOffset, buffer, testLength); + for (int i = 0; i < testLength; ++i) { + EXPECT_EQ(buffer[i], peek8(startingOffset + i)); + } + unmap(); +} + +TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithMoveOut16_Peek16_ReadsData) +{ + map(); + const int testLength = 5; + const int startingOffset = 14; + ViUInt16 buffer[testLength] = { 0x0123, 0x0234, 0x00, 0x8675, 0xFEDC }; + moveOut16(startingOffset, buffer, testLength); + for (int i = 0; i < testLength; ++i) { + EXPECT_EQ(buffer[i], peek16(startingOffset + i * 2)); + } + unmap(); +} + +TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithMoveOut32_Peek32_ReadsData) +{ + map(); + const int testLength = 5; + const int startingOffset = 16; + ViUInt32 buffer[testLength] = { 0x01234567, 0x02340234, 0x00, 0x86754321, 0xC0FFEE }; + moveOut32(startingOffset, buffer, testLength); + for (int i = 0; i < testLength; ++i) { + EXPECT_EQ(buffer[i], peek32(startingOffset + i * 4)); + } + unmap(); +} + +TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithMoveOut64_Peek64_ReadsData) +{ + map(); + const int testLength = 5; + const int startingOffset = 16; + ViUInt64 buffer[testLength] = { 0x01234567DEADBEEF, 0x02340234, 0x00, 0x867543210ABCDEF, 0xC0FFEE }; + moveOut64(startingOffset, buffer, testLength); + for (int i = 0; i < testLength; ++i) { + EXPECT_EQ(buffer[i], peek64(startingOffset + i * 8)); + } + unmap(); +} + +TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithPoke8_MoveIn8_ReadsData) +{ + map(); + const int testLength = 5; + const int startingOffset = 11; + ViUInt8 buffer[testLength] = { 0x01, 0x02, 0x00, 0x03, 0xFE }; + for (int i = 0; i < testLength; ++i) { + poke8(startingOffset + i, buffer[i]); + } + auto readBuffer = moveIn8(startingOffset, testLength); + for (int i = 0; i < testLength; ++i) { + EXPECT_EQ(readBuffer[i], buffer[i]); + } + unmap(); +} + +TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithPoke16_MoveIn16_ReadsData) +{ + map(); + const int testLength = 5; + const int startingOffset = 14; + ViUInt16 buffer[testLength] = { 0x0123, 0x0234, 0x00, 0x8675, 0xFEDC }; + for (int i = 0; i < testLength; ++i) { + poke16(startingOffset + i * 2, buffer[i]); + } + auto readBuffer = moveIn16(startingOffset, testLength); + for (int i = 0; i < testLength; ++i) { + EXPECT_EQ(readBuffer[i], buffer[i]); + } + unmap(); +} + +TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithPoke32_MoveIn32_ReadsData) +{ + map(); + const int testLength = 5; + const int startingOffset = 16; + ViUInt32 buffer[testLength] = { 0x01234567, 0x02340234, 0x00, 0x86754321, 0xC0FFEE }; + for (int i = 0; i < testLength; ++i) { + poke32(startingOffset + i * 4, buffer[i]); + } + auto readBuffer = moveIn32(startingOffset, testLength); + for (int i = 0; i < testLength; ++i) { + EXPECT_EQ(readBuffer[i], buffer[i]); + } + unmap(); +} + +TEST_F(VisaRegisterBasedLoopbackTest, DataWrittenWithPoke64_MoveIn64_ReadsData) +{ + map(); + const int testLength = 5; + const int startingOffset = 16; + ViUInt64 buffer[testLength] = { 0x01234567DEADBEEF, 0x02340234, 0x00, 0x867543210ABCDEF, 0xC0FFEE }; + for (int i = 0; i < testLength; ++i) { + poke64(startingOffset + i * 8, buffer[i]); + } + auto readBuffer = moveIn64(startingOffset, testLength); + for (int i = 0; i < testLength; ++i) { + EXPECT_EQ(readBuffer[i], buffer[i]); + } + unmap(); +} + } // namespace system } // namespace tests } // namespace ni diff --git a/source/tests/system/visa_session_tests.cpp b/source/tests/system/visa_session_tests.cpp index 56adb26b3..1f9d342a7 100644 --- a/source/tests/system/visa_session_tests.cpp +++ b/source/tests/system/visa_session_tests.cpp @@ -88,11 +88,24 @@ TEST_F(VisaSessionTest, OpenSession_CloseSession_ClosesDriverSession) TEST_F(VisaSessionTest, OpenWithErrorFromDriver_ReturnsDriverErrorWithUserErrorMessage) { - EXPECT_THROW_DRIVER_ERROR_WITH_SUBSTR({ - visa::OpenResponse init_response; - call_open(kVisaTestInvalidInstrumentDescriptor, visa::LOCK_STATE_NO_LOCK, "", 0, &init_response); - }, - kInvalidRsrc, kVisaErrorInstrumentDescriptorNotFoundMessage); + EXPECT_THROW_DRIVER_ERROR_WITH_SUBSTR( + { + visa::OpenResponse init_response; + call_open(kVisaTestInvalidInstrumentDescriptor, visa::LOCK_STATE_NO_LOCK, "", 0, &init_response); + }, + kInvalidRsrc, + kVisaErrorInstrumentDescriptorNotFoundMessage); +} + +TEST_F(VisaSessionTest, OpenWithErrorFromDriver_ReturnsDriverErrorWithResourceDescriptor) +{ + EXPECT_THROW_DRIVER_ERROR_WITH_SUBSTR( + { + visa::OpenResponse init_response; + call_open(kVisaTestInvalidInstrumentDescriptor, visa::LOCK_STATE_NO_LOCK, "", 0, &init_response); + }, + kInvalidRsrc, + kVisaTestInvalidInstrumentDescriptor); } TEST_F(VisaSessionTest, InvalidSession_CloseSession_ReturnsWarning) diff --git a/source/tests/utilities/tcp_echo_server.h b/source/tests/utilities/tcp_echo_server.h index 975bb31f3..464d3bb8e 100644 --- a/source/tests/utilities/tcp_echo_server.h +++ b/source/tests/utilities/tcp_echo_server.h @@ -21,12 +21,12 @@ class TcpEchoSession : public std::enable_shared_from_this { public: TcpEchoSession(SOCKET socket_fd) : socket_fd_(socket_fd) - , stop_session_(false) + , stop_echoing_(false) { } - ~TcpEchoSession() { - stop(); + ~TcpEchoSession() + { #ifdef _WIN32 closesocket(socket_fd_); #else @@ -34,45 +34,39 @@ class TcpEchoSession : public std::enable_shared_from_this { #endif } - void start() + void run() { - while (!stop_session_) { - do_read(); + while (!stop_echoing_) { + do_echo(); } } - void stop() + void prepare_stop() { - stop_session_ = true; + stop_echoing_ = true; } private: - void do_read() + void do_echo() { auto self(shared_from_this()); char buffer[max_length] = {0}; int n = recv(socket_fd_, buffer, max_length, 0); if (n > 0) { - do_write(buffer, n); + send(socket_fd_, buffer, n, 0); } } - void do_write(const char* data, int length) - { - auto self(shared_from_this()); - int n = send(socket_fd_, data, length, 0); - } - static const int max_length = 256; SOCKET socket_fd_; - bool stop_session_; + bool stop_echoing_; }; class TcpEchoServer { public: TcpEchoServer() - : server_fd_(-1) + : listening_fd_(-1) { } @@ -85,6 +79,11 @@ class TcpEchoServer return start_server_session(); } + void prepare_stop() + { + session_->prepare_stop(); + } + void stop() { close_server_session(); @@ -94,7 +93,7 @@ class TcpEchoServer { struct sockaddr_in assigned_address; socklen_t len = sizeof(assigned_address); - if (getsockname(server_fd_, (struct sockaddr *)&assigned_address, &len) == -1) { + if (getsockname(listening_fd_, (struct sockaddr *)&assigned_address, &len) == -1) { return -1; } else { @@ -109,8 +108,8 @@ class TcpEchoServer WSADATA wsa_data; WSAStartup(MAKEWORD(2, 2), &wsa_data); #endif - server_fd_ = socket(AF_INET, SOCK_STREAM, 0); - if (server_fd_ == -1) { + listening_fd_ = socket(AF_INET, SOCK_STREAM, 0); + if (listening_fd_ == -1) { return -1; } struct sockaddr_in server_address; @@ -119,43 +118,39 @@ class TcpEchoServer server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); server_address.sin_port = htons(0); - if (bind(server_fd_, (struct sockaddr*)&server_address, sizeof(server_address)) != 0) { + if (bind(listening_fd_, (struct sockaddr*)&server_address, sizeof(server_address)) != 0) { return -1; } - server_thread_ = std::thread(&TcpEchoServer::run_server, this, server_fd_); - return 0; - } - - void run_server(SOCKET server_fd) { - listen(server_fd, 1); - SOCKET client_fd = accept(server_fd, NULL, NULL); - if (client_fd == -1) { - return; + if (listen(listening_fd_, 1) != 0) { + return -1; } - std::thread([this, client_fd]() { - handle_client(client_fd); - }).detach(); + server_thread_ = std::thread(&TcpEchoServer::handle_client, this); + return 0; } void close_server_session() { - session_->stop(); server_thread_.join(); + session_.reset(); + #ifdef _WIN32 - closesocket(server_fd_); + closesocket(listening_fd_); WSACleanup(); #else - close(server_fd_); + close(listening_fd_); #endif } - void handle_client(SOCKET client_fd) + void handle_client() { - session_ = std::make_shared(client_fd); - session_->start(); + SOCKET client_fd = accept(listening_fd_, NULL, NULL); + if (client_fd != -1) { + session_ = std::make_shared(client_fd); + session_->run(); + } } - SOCKET server_fd_; + SOCKET listening_fd_; std::thread server_thread_; std::shared_ptr session_; };