--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ *
+ * http://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 "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "bluetooth/bluetooth_util.h"
+#include "bluetooth/ut/uuid.h"
+#include "bluetooth/uuid.h"
+
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+#include "common/logger.h"
+#include "common/platform_result.h"
+#include "common/scope_exit.h"
+
+#if __cplusplus > 201402L
+#include <optional>
+using std::optional;
+#else
+#include "common/optional.h"
+using common::optional;
+#endif
+
+using namespace std;
+using testing::_;
+using testing::StrEq;
+using testing::internal::ColoredPrintf;
+using testing::internal::GTestColor;
+
+using namespace extension::bluetooth;
+using namespace extension::bluetooth::util;
+
+class UUIDTest : public testing::Test {
+ protected:
+ virtual void SetUp() override {
+ invalid_uuids_ = {
+ "", "1", "12", "123", // less than 4 digits
+ "12345", "123456", "1234567", // less than 8 digits
+ "123456789", // more than 8 digits
+ "1ga2", "1%22", "1 abd", // invalid characters
+ "not_a_uuid_123", "09i0821AF7-30A0-1C00-8F0B-00805F9B34FB",
+ "09b0821AF7 30A0-1C00-8F0B-00805F9B34FB",
+ "109b0821AF7-30A0-1C00-8F0B-00805F9B34FB" // too long
+ "09A0821AF730A01C008F0B00805F9B34FB" // no hyphens
+ };
+
+ const std::string kTrailing96b = "-0000-1000-8000-00805f9b34fb";
+
+ valid_uuids_ = {
+ /* 16 bit UUIDs */
+ UUIDFormats{"1ab2", "00001ab2" + kTrailing96b, "00001ab2"s, "1ab2"s},
+ UUIDFormats{"0000", "00000000" + kTrailing96b, "00000000"s, "0000"s}, // zero
+ UUIDFormats{"0001", "00000001" + kTrailing96b, "00000001"s, "0001"s}, // leading zeros
+ UUIDFormats{"AabB", "0000aabb" + kTrailing96b, "0000aabb"s, "aabb"s}, // mixed case digits
+ UUIDFormats{"c1e0", "0000c1e0" + kTrailing96b, "0000c1e0"s, "c1e0"s}, // lower case digits
+ UUIDFormats{"C1E0", "0000c1e0" + kTrailing96b, "0000c1e0"s, "c1e0"s}, // upper case digits
+
+ /* 32 bit UUIDs */
+ UUIDFormats{"1ab203bd", "1ab203bd" + kTrailing96b, "1ab203bd"s, {}},
+ UUIDFormats{"0001391f", "0001391f" + kTrailing96b, "0001391f"s, {}}, // leading zeros
+ UUIDFormats{"AabB1bDC", "aabb1bdc" + kTrailing96b, "aabb1bdc"s, {}}, // mixed case digits
+ UUIDFormats{"aabb1bdc", "aabb1bdc" + kTrailing96b, "aabb1bdc"s, {}}, // lower case digits
+ UUIDFormats{"AABB1BDC", "aabb1bdc" + kTrailing96b, "aabb1bdc"s, {}}, // upper case digits
+
+ /* 128 bit-only UUIDs */
+ UUIDFormats{"00000000-0000-0000-0000-000000000000",
+ "00000000-0000-0000-0000-000000000000",
+ {},
+ {}}, // zero
+ UUIDFormats{"10821Af7-30a0-1C00-8f0B-00805f9B34Fb",
+ "10821af7-30a0-1c00-8f0b-00805f9b34fb",
+ {},
+ {}}, // mixed case digits
+ UUIDFormats{"10821af7-30a0-1c00-8f0b-00805f9b34fb",
+ "10821af7-30a0-1c00-8f0b-00805f9b34fb",
+ {},
+ {}}, // lower case digits
+ {"10821AF7-30A0-1C00-8F0B-00805F9B34FB",
+ "10821af7-30a0-1c00-8f0b-00805f9b34fb",
+ {},
+ {}} // upper case digits
+ };
+ }
+
+ virtual void TearDown() override {
+ }
+
+ static const int StringInit = 0;
+ static const int Format128 = 1;
+ static const int Format32 = 2;
+ static const int Format16 = 3;
+
+ /*
+ * Values of the tuple are indexed with Init and Format* constant defined
+ * above.
+ * The values contain:
+ * StringInit: the argument passed to UUID constructor
+ * Format128: the valid value that should be stored in UUID::uuid_128_bit
+ * Format16: the valid value that should be returned by UUID::To16Bit()
+ * Format32: the valid value that should be returned by UUID::To32Bit()
+ */
+ using UUIDFormats = std::tuple<std::string, // string init value
+ std::string, // 128 bit
+ optional<std::string>, // 32 bit
+ optional<std::string>>; // 16 bit
+ std::vector<UUIDFormats> valid_uuids_;
+ std::vector<std::string> invalid_uuids_;
+
+ struct BTHelper {
+ struct BTInitializationError : std::runtime_error {
+ using std::runtime_error::runtime_error;
+ };
+
+ BTHelper(const std::vector<UUIDFormats>& valid_uuids) {
+ auto ret = bt_initialize();
+ if (ret != BT_ERROR_NONE) {
+ throw BTInitializationError{"bt_initialize() failed: " + GetBluetoothErrorMessage(ret)};
+ }
+
+ auto adapter_state = BT_ADAPTER_DISABLED;
+ ret = bt_adapter_get_state(&adapter_state);
+ if (ret != BT_ERROR_NONE) {
+ throw BTInitializationError{"bt_adapter_get_state() failed: " +
+ GetBluetoothErrorMessage(ret)};
+ }
+
+ if (adapter_state == BT_ADAPTER_DISABLED) {
+ throw BTInitializationError{
+ "Bluetooth adapter is disabled."
+ " Enable it before running tests."};
+ }
+
+ ret = bt_gatt_service_create(std::get<Format16>(valid_uuids[0])->c_str(),
+ BT_GATT_SERVICE_TYPE_PRIMARY, &gatt_handle_16_bit);
+ if (ret != BT_ERROR_NONE) {
+ throw BTInitializationError{"Could not create bt_gatt_handle from 16 bit UUID: " +
+ GetBluetoothErrorMessage(ret)};
+ }
+
+ ret = bt_gatt_service_create(std::get<Format32>(valid_uuids[0])->c_str(),
+ BT_GATT_SERVICE_TYPE_PRIMARY, &gatt_handle_32_bit);
+ if (ret != BT_ERROR_NONE) {
+ throw BTInitializationError{"Could not create bt_gatt_handle from 32 bit UUID: " +
+ GetBluetoothErrorMessage(ret)};
+ }
+
+ ret = bt_gatt_service_create(std::get<Format128>(valid_uuids[0]).c_str(),
+ BT_GATT_SERVICE_TYPE_PRIMARY, &gatt_handle_128_bit);
+ if (ret != BT_ERROR_NONE) {
+ throw BTInitializationError{"Could not create bt_gatt_handle from 128 bit UUID: " +
+ GetBluetoothErrorMessage(ret)};
+ }
+ }
+
+ ~BTHelper() {
+ auto ret = bt_gatt_service_destroy(gatt_handle_16_bit);
+ if (ret != BT_ERROR_NONE) {
+ ColoredPrintf(GTestColor::COLOR_YELLOW, "Could not destroy gatt_handle_16_bit %s",
+ GetBluetoothErrorMessage(ret).c_str());
+ }
+
+ ret = bt_gatt_service_destroy(gatt_handle_32_bit);
+ if (ret != BT_ERROR_NONE) {
+ ColoredPrintf(GTestColor::COLOR_YELLOW, "Could not destroy gatt_handle_32_bit %s",
+ GetBluetoothErrorMessage(ret).c_str());
+ }
+
+ ret = bt_gatt_service_destroy(gatt_handle_128_bit);
+ if (ret != BT_ERROR_NONE) {
+ ColoredPrintf(GTestColor::COLOR_YELLOW, "Could not destroy gatt_handle_128_bit %s",
+ GetBluetoothErrorMessage(ret).c_str());
+ }
+
+ ret = bt_deinitialize();
+ if (ret != BT_ERROR_NONE) {
+ ColoredPrintf(GTestColor::COLOR_YELLOW, "bt_deinitialize failed: %s",
+ GetBluetoothErrorMessage(ret).c_str());
+ }
+ }
+
+ bt_gatt_h gatt_handle_16_bit;
+ bt_gatt_h gatt_handle_32_bit;
+ bt_gatt_h gatt_handle_128_bit;
+ };
+};
+
+TEST_F(UUIDTest, CreateFromString) {
+ ScopeLogger("CreateFromString");
+
+ for (const auto& uuid : valid_uuids_) {
+ auto uuid_obj = UUID::create(std::get<StringInit>(uuid));
+ EXPECT_TRUE(uuid_obj) << "Failed for UUID: " << std::get<StringInit>(uuid);
+ }
+
+ for (const auto& invalid_uuid : invalid_uuids_) {
+ auto uuid_obj = UUID::create(invalid_uuid);
+ EXPECT_FALSE(uuid_obj) << "Failed for UUID: " << invalid_uuid;
+ }
+}
+
+TEST_F(UUIDTest, CreateFromCString) {
+ ScopeLogger("CreateFromCString");
+
+ for (const auto& uuid : valid_uuids_) {
+ auto uuid_obj = UUID::create(std::get<StringInit>(uuid).c_str());
+ EXPECT_TRUE(uuid_obj) << "Failed for UUID: " << std::get<StringInit>(uuid).c_str();
+ }
+
+ for (const auto& invalid_uuid : invalid_uuids_) {
+ auto uuid_obj = UUID::create(invalid_uuid.c_str());
+ EXPECT_FALSE(uuid_obj) << "Failed for UUID: " << invalid_uuid.c_str();
+ }
+}
+
+TEST_F(UUIDTest, CreateFromGATTHandle) {
+ ScopeLogger("CreateFromGATTHandle");
+
+ BTHelper bt_helper{valid_uuids_};
+
+ const auto valid_uuid_128_bit = std::get<Format128>(valid_uuids_[0]);
+
+ auto uuid_from_16_bit = UUID::createFromGatt(bt_helper.gatt_handle_16_bit);
+ ASSERT_TRUE(uuid_from_16_bit);
+ EXPECT_EQ(uuid_from_16_bit->uuid_128_bit, valid_uuid_128_bit);
+
+ auto uuid_from_32_bit = UUID::createFromGatt(bt_helper.gatt_handle_32_bit);
+ ASSERT_TRUE(uuid_from_32_bit);
+ EXPECT_EQ(uuid_from_32_bit->uuid_128_bit, valid_uuid_128_bit);
+
+ auto uuid_from_128_bit = UUID::createFromGatt(bt_helper.gatt_handle_128_bit);
+ ASSERT_TRUE(uuid_from_128_bit);
+ EXPECT_EQ(uuid_from_128_bit->uuid_128_bit, valid_uuid_128_bit);
+}
+
+TEST_F(UUIDTest, ToXXXBits) {
+ for (const auto& uuid : valid_uuids_) {
+ auto uuid_obj = UUID::create(std::get<StringInit>(uuid));
+
+ /* Assert prevents the crash caused by referencing uuid_obj with no value */
+ ASSERT_TRUE(uuid_obj) << "Failed for UUID: " << std::get<StringInit>(uuid);
+
+ EXPECT_EQ(uuid_obj->To16Bit(), std::get<Format16>(uuid))
+ << "Failed for UUID: " << std::get<Format128>(uuid) << " expected: ("
+ << (std::get<Format16>(uuid) ? *std::get<Format16>(uuid) : "<NO UUID>") + ")"
+ << " got: (" << (uuid_obj->To16Bit() ? *uuid_obj->To16Bit() : "<NO UUID>") + ")";
+ EXPECT_EQ(uuid_obj->To32Bit(), std::get<Format32>(uuid))
+ << "Failed for UUID: " << std::get<Format128>(uuid) << " expected: ("
+ << (std::get<Format32>(uuid) ? *std::get<Format32>(uuid) : "<NO UUID>") + ")"
+ << " got: (" << (uuid_obj->To32Bit() ? *uuid_obj->To32Bit() : "<NO UUID>") + ")";
+ EXPECT_EQ(uuid_obj->uuid_128_bit, std::get<Format128>(uuid))
+ << "Failed for UUID: " << std::get<Format128>(uuid) << " expected: ("
+ << std::get<Format128>(uuid) << ")"
+ << " got: (" << uuid_obj->uuid_128_bit;
+ }
+}
+
+TEST_F(UUIDTest, EqualsOperatorEqualUUIDs) {
+ const auto uuid_from_16_bit = UUID::create(*std::get<Format16>(valid_uuids_[0]));
+ const auto uuid_from_32_bit = UUID::create(*std::get<Format32>(valid_uuids_[0]));
+ const auto uuid_from_128_bit = UUID::create(std::get<Format128>(valid_uuids_[0]));
+
+ /*
+ * UUIDs created from strings of different lengths should all be equal.
+ */
+ EXPECT_EQ(uuid_from_16_bit, uuid_from_16_bit);
+ EXPECT_EQ(uuid_from_16_bit, uuid_from_32_bit);
+ EXPECT_EQ(uuid_from_16_bit, uuid_from_128_bit);
+ EXPECT_EQ(uuid_from_32_bit, uuid_from_32_bit);
+ EXPECT_EQ(uuid_from_32_bit, uuid_from_128_bit);
+ EXPECT_EQ(uuid_from_128_bit, uuid_from_128_bit);
+}
+
+TEST_F(UUIDTest, EqualsOperatorUnequalUUIDs) {
+ const auto uuid_A_from_16_bit = UUID::create(*std::get<Format16>(valid_uuids_[0]));
+ const auto uuid_A_from_32_bit = UUID::create(*std::get<Format32>(valid_uuids_[0]));
+ const auto uuid_A_from_128_bit = UUID::create(std::get<Format128>(valid_uuids_[0]));
+
+ const auto uuid_B_from_16_bit = UUID::create(*std::get<Format16>(valid_uuids_[1]));
+ const auto uuid_B_from_32_bit = UUID::create(*std::get<Format32>(valid_uuids_[1]));
+ const auto uuid_B_from_128_bit = UUID::create(std::get<Format128>(valid_uuids_[1]));
+
+ EXPECT_FALSE(uuid_A_from_16_bit == uuid_B_from_16_bit);
+ EXPECT_FALSE(uuid_A_from_16_bit == uuid_B_from_32_bit);
+ EXPECT_FALSE(uuid_A_from_16_bit == uuid_B_from_128_bit);
+ EXPECT_FALSE(uuid_A_from_32_bit == uuid_B_from_32_bit);
+ EXPECT_FALSE(uuid_A_from_32_bit == uuid_B_from_128_bit);
+ EXPECT_FALSE(uuid_A_from_128_bit == uuid_B_from_128_bit);
+}
+
+TEST_F(UUIDTest, ShortestPossibleFormat) {
+ for (const auto& uuid : valid_uuids_) {
+ auto uuid_obj = UUID::create(std::get<StringInit>(uuid));
+
+ /* Assert prevents the crash caused by referencing uuid_obj with no value */
+ ASSERT_TRUE(uuid_obj) << "Failed for UUID: " << std::get<StringInit>(uuid);
+
+ const auto shortest_uuid_format = uuid_obj->ShortestPossibleFormat();
+ if (std::get<Format16>(uuid)) {
+ EXPECT_EQ(*std::get<Format16>(uuid), shortest_uuid_format);
+ } else if (std::get<Format32>(uuid)) {
+ EXPECT_EQ(*std::get<Format32>(uuid), shortest_uuid_format);
+ } else {
+ EXPECT_EQ(std::get<Format128>(uuid), shortest_uuid_format);
+ }
+ }
+}