diff --git a/Package.swift b/Package.swift index 6408dc32..64cd18c5 100644 --- a/Package.swift +++ b/Package.swift @@ -305,6 +305,7 @@ let package = Package( "connections/implementation/bluetooth_bwu_test.cc", "connections/implementation/wifi_direct_bwu_test.cc", "connections/implementation/wifi_hotspot_bwu_test.cc", + "connections/implementation/wifi_lan_bwu_handler_test.cc", "connections/implementation/analytics/analytics_recorder_test.cc", "connections/implementation/analytics/throughput_recorder_test.cc", "connections/implementation/mediums/advertisements/data_element_test.cc", diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index 0ad6bbf1..7bb3721b 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -602,3 +602,22 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "wifi_lan_bwu_handler_test", + srcs = [ + "wifi_lan_bwu_handler_test.cc", + ], + deps = [ + ":internal", + "//connections/implementation/mediums", + "//internal/platform:base", + "//internal/platform:mock_platform", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:platform", + "//internal/platform/implementation:platform_impl", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings:string_view", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/connections/implementation/wifi_lan_bwu_handler_test.cc b/connections/implementation/wifi_lan_bwu_handler_test.cc new file mode 100644 index 00000000..07dc665e --- /dev/null +++ b/connections/implementation/wifi_lan_bwu_handler_test.cc @@ -0,0 +1,244 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "connections/implementation/wifi_lan_bwu_handler.h" + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "connections/implementation/bwu_handler.h" +#include "connections/implementation/client_proxy.h" +#include "connections/implementation/mediums/mediums.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/implementation/platform.h" +#include "internal/platform/implementation/wifi_lan.h" +#include "internal/platform/mock_input_stream.h" +#include "internal/platform/mock_output_stream.h" +#include "internal/platform/mock_wifi_lan_medium.h" +#include "internal/platform/mock_wifi_lan_server_socket.h" +#include "internal/platform/mock_wifi_lan_socket.h" + +namespace nearby { + +MockWifiLanMedium* wifi_lan_medium = nullptr; + +namespace connections { +namespace { +using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; +using ::location::nearby::connections::OfflineFrame; +using ::location::nearby::connections::V1Frame; +using ::location::nearby::proto::connections::OperationResultCode; +using ::testing::_; +using ::testing::ByMove; +using ::protobuf_matchers::EqualsProto; +using ::testing::InSequence; +using ::testing::MockFunction; +using ::testing::Return; +using ::testing::ReturnRef; + +constexpr absl::string_view kIpv4Address{"\xc0\xa8\x00\x01", 4}; +constexpr absl::string_view kIpv6Address{ + "\x2a\x00\x79\xe0\x2e\x87\x00\x06\xb7\x28\x67\x45\x7a\xdd\x01\x53", 16}; +constexpr absl::string_view kServiceId{"service_id"}; +constexpr absl::string_view kEndpointId{"endpoint_id"}; + +class WifiLanBwuHandlerTest : public ::testing::Test { + protected: + WifiLanBwuHandlerTest() + : handler_(mediums_, incoming_connection_callback_.AsStdFunction()) {} + + Mediums mediums_; + MockFunction)> + incoming_connection_callback_; + WifiLanBwuHandler handler_; +}; + +TEST_F(WifiLanBwuHandlerTest, + CreateUpgradedEndpointChannel_EmptyPathInfo_Fails) { + ClientProxy client; + BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; + // Create an empty wifi_lan_socket. + path_info.mutable_wifi_lan_socket(); + + auto result = handler_.CreateUpgradedEndpointChannel( + &client, "service_id", "endpoint_id", std::move(path_info)); + + ASSERT_TRUE(result.has_error()); + EXPECT_EQ(result.error().operation_result_code().value(), + OperationResultCode::CONNECTIVITY_WIFI_LAN_IP_ADDRESS_ERROR); +}; + +TEST_F(WifiLanBwuHandlerTest, CreateUpgradedEndpointChannel_IpAddress_Success) { + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + MockInputStream input_stream; + MockOutputStream output_stream; + auto wifi_lan_socket = std::make_unique(); + EXPECT_CALL(*wifi_lan_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(input_stream)); + EXPECT_CALL(*wifi_lan_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(output_stream)); + EXPECT_CALL(*wifi_lan_medium, IsNetworkConnected()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*wifi_lan_medium, + ConnectToService(std::string(kIpv4Address), 8080, _)) + .WillOnce(Return(ByMove(std::move(wifi_lan_socket)))); + + BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; + path_info.mutable_wifi_lan_socket()->set_ip_address(kIpv4Address); + path_info.mutable_wifi_lan_socket()->set_wifi_port(8080); + + auto result = handler_.CreateUpgradedEndpointChannel( + &client, std::string(kServiceId), std::string(kEndpointId), + std::move(path_info)); + + EXPECT_TRUE(result.has_value()); +}; + +TEST_F(WifiLanBwuHandlerTest, + CreateUpgradedEndpointChannel_AddressCandidates_FirstCandidate_Success) { + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + MockInputStream input_stream; + MockOutputStream output_stream; + auto wifi_lan_socket = std::make_unique(); + EXPECT_CALL(*wifi_lan_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(input_stream)); + EXPECT_CALL(*wifi_lan_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(output_stream)); + EXPECT_CALL(*wifi_lan_medium, IsNetworkConnected()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*wifi_lan_medium, + ConnectToService(std::string(kIpv6Address), 8080, _)) + .WillOnce(Return(ByMove(std::move(wifi_lan_socket)))); + + BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; + auto* address_candidate = + path_info.mutable_wifi_lan_socket()->add_address_candidates(); + address_candidate->set_ip_address(kIpv6Address); + address_candidate->set_port(8080); + address_candidate = + path_info.mutable_wifi_lan_socket()->add_address_candidates(); + address_candidate->set_ip_address(kIpv4Address); + address_candidate->set_port(8080); + + auto result = handler_.CreateUpgradedEndpointChannel( + &client, std::string(kServiceId), std::string(kEndpointId), + std::move(path_info)); + + EXPECT_TRUE(result.has_value()); +}; + +TEST_F(WifiLanBwuHandlerTest, + CreateUpgradedEndpointChannel_AddressCandidates_FirstCandidate_Fails) { + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + MockInputStream input_stream; + MockOutputStream output_stream; + auto wifi_lan_socket = std::make_unique(); + EXPECT_CALL(*wifi_lan_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(input_stream)); + EXPECT_CALL(*wifi_lan_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(output_stream)); + EXPECT_CALL(*wifi_lan_medium, IsNetworkConnected()) + .WillRepeatedly(Return(true)); + InSequence seq; + EXPECT_CALL(*wifi_lan_medium, + ConnectToService(std::string(kIpv6Address), 8080, _)) + .WillOnce(Return(ByMove(nullptr))); + EXPECT_CALL(*wifi_lan_medium, + ConnectToService(std::string(kIpv4Address), 8080, _)) + .WillOnce(Return(ByMove(std::move(wifi_lan_socket)))); + + BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; + auto* address_candidate = + path_info.mutable_wifi_lan_socket()->add_address_candidates(); + address_candidate->set_ip_address(kIpv6Address); + address_candidate->set_port(8080); + address_candidate = + path_info.mutable_wifi_lan_socket()->add_address_candidates(); + address_candidate->set_ip_address(kIpv4Address); + address_candidate->set_port(8080); + + auto result = handler_.CreateUpgradedEndpointChannel( + &client, std::string(kServiceId), std::string(kEndpointId), + std::move(path_info)); + + EXPECT_TRUE(result.has_value()); +}; + +TEST_F(WifiLanBwuHandlerTest, InitializeUpgradedMediumForEndpoint_Success) { + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + auto wifi_lan_server_socket = std::make_unique(); + EXPECT_CALL(*wifi_lan_server_socket, GetPort()).WillRepeatedly(Return(8080)); + EXPECT_CALL(*wifi_lan_medium, IsNetworkConnected()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*wifi_lan_medium, ListenForService(_)) + .WillOnce(Return(ByMove(std::move(wifi_lan_server_socket)))); + EXPECT_CALL(*wifi_lan_medium, GetUpgradeAddressCandidates(_)) + .WillOnce(Return(std::vector{std::string(kIpv6Address), + std::string(kIpv4Address)})); + OfflineFrame expected_frame; + expected_frame.set_version(OfflineFrame::V1); + expected_frame.mutable_v1()->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION); + auto* bwu_frame = + expected_frame.mutable_v1()->mutable_bandwidth_upgrade_negotiation(); + bwu_frame->set_event_type( + BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE); + auto* upgrade_path_info = bwu_frame->mutable_upgrade_path_info(); + upgrade_path_info->set_medium( + BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WIFI_LAN); + auto* wifi_lan_socket = expected_frame.mutable_v1() + ->mutable_bandwidth_upgrade_negotiation() + ->mutable_upgrade_path_info() + ->mutable_wifi_lan_socket(); + wifi_lan_socket->set_ip_address(kIpv4Address); + wifi_lan_socket->set_wifi_port(8080); + auto* address_candidate = wifi_lan_socket->add_address_candidates(); + address_candidate->set_ip_address(kIpv6Address); + address_candidate->set_port(8080); + address_candidate = wifi_lan_socket->add_address_candidates(); + address_candidate->set_ip_address(kIpv4Address); + address_candidate->set_port(8080); + upgrade_path_info->set_supports_client_introduction_ack(true); + + ByteArray result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + + EXPECT_FALSE(result.Empty()); + OfflineFrame result_frame; + EXPECT_TRUE(result_frame.ParseFromString(std::string(result))); + EXPECT_THAT(result_frame, EqualsProto(expected_frame)); +} + +} // namespace +} // namespace connections +namespace api { + +std::unique_ptr ImplementationPlatform::CreateWifiLanMedium() { + auto medium = std::make_unique(); + wifi_lan_medium = medium.get(); + return medium; +} + +} // namespace api +} // namespace nearby diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 2d0d6cfb..9c5ef481 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -388,6 +388,26 @@ cc_library( ], ) +cc_library( + name = "mock_platform", + testonly = True, + hdrs = [ + "mock_input_stream.h", + "mock_output_stream.h", + "mock_wifi_lan_medium.h", + "mock_wifi_lan_server_socket.h", + "mock_wifi_lan_socket.h", + ], + visibility = ["//visibility:public"], + deps = [ + ":base", + ":cancellation_flag", + "//internal/platform/implementation:comm", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_for_library_testonly", + ], +) + cc_test( name = "platform_base_test", srcs = [ diff --git a/internal/platform/mock_input_stream.h b/internal/platform/mock_input_stream.h new file mode 100644 index 00000000..8e7f1ef8 --- /dev/null +++ b/internal/platform/mock_input_stream.h @@ -0,0 +1,34 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_INPUT_STREAM_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_INPUT_STREAM_H_ + +#include +#include "gmock/gmock.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/exception.h" +#include "internal/platform/input_stream.h" + +namespace nearby { + +class MockInputStream : public InputStream { + public: + MOCK_METHOD(ExceptionOr, Read, (std::int64_t size), (override)); + MOCK_METHOD(Exception, Close, (), (override)); +}; + +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_INPUT_STREAM_H_ diff --git a/internal/platform/mock_output_stream.h b/internal/platform/mock_output_stream.h new file mode 100644 index 00000000..cbcf16e8 --- /dev/null +++ b/internal/platform/mock_output_stream.h @@ -0,0 +1,34 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_OUTPUT_STREAM_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_OUTPUT_STREAM_H_ + +#include "gmock/gmock.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/exception.h" +#include "internal/platform/output_stream.h" + +namespace nearby { + +class MockOutputStream : public OutputStream { + public: + MOCK_METHOD(Exception, Write, (const ByteArray& data), (override)); + MOCK_METHOD(Exception, Flush, (), (override)); + MOCK_METHOD(Exception, Close, (), (override)); +}; + +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_OUTPUT_STREAM_H_ diff --git a/internal/platform/mock_wifi_lan_medium.h b/internal/platform/mock_wifi_lan_medium.h new file mode 100644 index 00000000..8b759969 --- /dev/null +++ b/internal/platform/mock_wifi_lan_medium.h @@ -0,0 +1,63 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_MEDIUM_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_MEDIUM_H_ + +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "gmock/gmock.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/implementation/wifi_lan.h" +#include "internal/platform/nsd_service_info.h" + +namespace nearby { + +class MockWifiLanMedium : public api::WifiLanMedium { + public: + MOCK_METHOD(bool, IsNetworkConnected, (), (const, override)); + MOCK_METHOD(bool, StartAdvertising, (const NsdServiceInfo& nsd_service_info), + (override)); + MOCK_METHOD(bool, StopAdvertising, (const NsdServiceInfo& nsd_service_info), + (override)); + MOCK_METHOD(bool, StartDiscovery, + (const std::string& service_type, + DiscoveredServiceCallback callback), + (override)); + MOCK_METHOD(bool, StopDiscovery, (const std::string& service_type), + (override)); + MOCK_METHOD(std::unique_ptr, ConnectToService, + (const NsdServiceInfo& remote_service_info, + CancellationFlag* cancellation_flag), + (override)); + MOCK_METHOD(std::unique_ptr, ConnectToService, + (const std::string& ip_address, int port, + CancellationFlag* cancellation_flag), + (override)); + MOCK_METHOD(std::unique_ptr, ListenForService, + (int port), (override)); + MOCK_METHOD((absl::optional>), + GetDynamicPortRange, (), (override)); + MOCK_METHOD(std::vector, GetUpgradeAddressCandidates, + (const api::WifiLanServerSocket& server_socket), (override)); +}; + +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_MEDIUM_H_ diff --git a/internal/platform/mock_wifi_lan_server_socket.h b/internal/platform/mock_wifi_lan_server_socket.h new file mode 100644 index 00000000..2aab10e6 --- /dev/null +++ b/internal/platform/mock_wifi_lan_server_socket.h @@ -0,0 +1,37 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_SERVER_SOCKET_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_SERVER_SOCKET_H_ + +#include +#include + +#include "gmock/gmock.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/wifi_lan.h" + +namespace nearby { + +class MockWifiLanServerSocket : public api::WifiLanServerSocket { + public: + MOCK_METHOD(std::string, GetIPAddress, (), (const, override)); + MOCK_METHOD(int, GetPort, (), (const, override)); + MOCK_METHOD(std::unique_ptr, Accept, (), (override)); + MOCK_METHOD(Exception, Close, (), (override)); +}; + +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_SERVER_SOCKET_H_ diff --git a/internal/platform/mock_wifi_lan_socket.h b/internal/platform/mock_wifi_lan_socket.h new file mode 100644 index 00000000..b3e29d8a --- /dev/null +++ b/internal/platform/mock_wifi_lan_socket.h @@ -0,0 +1,35 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_SOCKET_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_SOCKET_H_ + +#include "gmock/gmock.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/wifi_lan.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" + +namespace nearby { + +class MockWifiLanSocket : public api::WifiLanSocket { + public: + MOCK_METHOD(InputStream&, GetInputStream, (), (override)); + MOCK_METHOD(OutputStream&, GetOutputStream, (), (override)); + MOCK_METHOD(Exception, Close, (), (override)); +}; + +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_MOCK_WIFI_LAN_SOCKET_H_ diff --git a/internal/platform/wifi_credential.h b/internal/platform/wifi_credential.h index 5f900fc0..b619223c 100644 --- a/internal/platform/wifi_credential.h +++ b/internal/platform/wifi_credential.h @@ -66,8 +66,6 @@ class HotspotCredentials { private: std::string ssid_; std::string password_; - std::string gateway_; - int port_ = 0; int frequency_ = -1; std::vector address_candidates_; };