bluetooth_device_list_controller_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/system/bluetooth/bluetooth_device_list_controller_impl.h"
  5. #include <memory>
  6. #include <string>
  7. #include <vector>
  8. #include "ash/constants/ash_features.h"
  9. #include "ash/strings/grit/ash_strings.h"
  10. #include "ash/system/bluetooth/bluetooth_detailed_view.h"
  11. #include "ash/system/bluetooth/bluetooth_device_list_item_view.h"
  12. #include "ash/system/bluetooth/fake_bluetooth_detailed_view.h"
  13. #include "ash/system/tray/tri_view.h"
  14. #include "ash/test/ash_test_base.h"
  15. #include "base/test/scoped_feature_list.h"
  16. #include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
  17. #include "ui/base/l10n/l10n_util.h"
  18. #include "ui/views/controls/label.h"
  19. #include "ui/views/controls/separator.h"
  20. namespace ash {
  21. namespace {
  22. using chromeos::bluetooth_config::mojom::BluetoothDeviceProperties;
  23. using chromeos::bluetooth_config::mojom::DeviceConnectionState;
  24. using chromeos::bluetooth_config::mojom::PairedBluetoothDeviceProperties;
  25. using chromeos::bluetooth_config::mojom::PairedBluetoothDevicePropertiesPtr;
  26. const char kDeviceId1[] = "/device/id/1";
  27. const char kDeviceId2[] = "/device/id/2";
  28. const char kDeviceNickname[] = "mau5";
  29. } // namespace
  30. class BluetoothDeviceListControllerTest : public AshTestBase {
  31. public:
  32. void SetUp() override {
  33. AshTestBase::SetUp();
  34. feature_list_.InitAndEnableFeature(features::kBluetoothRevamp);
  35. fake_bluetooth_detailed_view_ =
  36. std::make_unique<FakeBluetoothDetailedView>(/*delegate=*/nullptr);
  37. bluetooth_device_list_controller_impl_ =
  38. std::make_unique<BluetoothDeviceListControllerImpl>(
  39. fake_bluetooth_detailed_view_.get());
  40. }
  41. void TearDown() override { AshTestBase::TearDown(); }
  42. const TriView* FindConnectedSubHeader() {
  43. return FindSubHeaderWithText(l10n_util::GetStringUTF16(
  44. IDS_ASH_STATUS_TRAY_BLUETOOTH_CURRENTLY_CONNECTED_DEVICES));
  45. }
  46. const TriView* FindPreviouslyConnectedSubHeader() {
  47. return FindSubHeaderWithText(l10n_util::GetStringUTF16(
  48. IDS_ASH_STATUS_TRAY_BLUETOOTH_PREVIOUSLY_CONNECTED_DEVICES));
  49. }
  50. const TriView* FindNoDeviceConnectedSubHeader() {
  51. return FindSubHeaderWithText(l10n_util::GetStringUTF16(
  52. IDS_ASH_STATUS_TRAY_BLUETOOTH_NO_DEVICE_CONNECTED));
  53. }
  54. const views::Separator* FindSeparator() {
  55. for (const auto* view : device_list()->children()) {
  56. if (!std::strcmp("Separator", view->GetClassName()))
  57. return static_cast<const views::Separator*>(view);
  58. }
  59. return nullptr;
  60. }
  61. PairedBluetoothDevicePropertiesPtr BuildDeviceProperties(
  62. const std::string& id) {
  63. PairedBluetoothDevicePropertiesPtr device_properties =
  64. PairedBluetoothDeviceProperties::New();
  65. device_properties->device_properties = BluetoothDeviceProperties::New();
  66. device_properties->device_properties->id = id;
  67. return device_properties;
  68. }
  69. const std::u16string& GetSubHeaderText(const TriView* sub_header) {
  70. EXPECT_TRUE(sub_header);
  71. EXPECT_EQ(1u, sub_header->children().at(1)->children().size());
  72. return static_cast<views::Label*>(
  73. sub_header->children().at(1)->children().at(0))
  74. ->GetText();
  75. }
  76. const char* GetDeviceId(const BluetoothDeviceListItemView* device_item_view) {
  77. return device_item_view->device_properties()->device_properties->id.c_str();
  78. }
  79. const BluetoothDeviceListItemView* GetFirstDeviceView() {
  80. EXPECT_LT(1u, device_list()->children().size());
  81. return static_cast<BluetoothDeviceListItemView*>(
  82. device_list()->children().at(1));
  83. }
  84. void CheckDeviceListOrdering(size_t connected_device_count,
  85. size_t previously_connected_device_count) {
  86. if (connected_device_count && previously_connected_device_count) {
  87. const TriView* connected_sub_header = FindConnectedSubHeader();
  88. const TriView* previously_connected_sub_header =
  89. FindPreviouslyConnectedSubHeader();
  90. const views::Separator* device_list_separator = FindSeparator();
  91. EXPECT_TRUE(connected_sub_header);
  92. EXPECT_TRUE(previously_connected_sub_header);
  93. EXPECT_TRUE(device_list_separator);
  94. const size_t connected_index =
  95. device_list()->GetIndexOf(connected_sub_header).value();
  96. const size_t previously_connected_index =
  97. device_list()->GetIndexOf(previously_connected_sub_header).value();
  98. const size_t separator_index =
  99. device_list()->GetIndexOf(device_list_separator).value();
  100. EXPECT_EQ(0u, connected_index);
  101. EXPECT_EQ(connected_device_count + 1, separator_index);
  102. EXPECT_EQ(separator_index + 1, previously_connected_index);
  103. return;
  104. }
  105. if (connected_device_count) {
  106. const TriView* connected_sub_header = FindConnectedSubHeader();
  107. EXPECT_TRUE(connected_sub_header);
  108. EXPECT_EQ(0u, device_list()->GetIndexOf(connected_sub_header));
  109. EXPECT_EQ(connected_device_count + 1, device_list()->children().size());
  110. return;
  111. }
  112. if (previously_connected_device_count) {
  113. const TriView* previously_connected_sub_header =
  114. FindPreviouslyConnectedSubHeader();
  115. EXPECT_TRUE(previously_connected_sub_header);
  116. EXPECT_EQ(0u, device_list()->GetIndexOf(previously_connected_sub_header));
  117. EXPECT_EQ(previously_connected_device_count + 1,
  118. device_list()->children().size());
  119. return;
  120. }
  121. const TriView* no_device_connected_sub_header =
  122. FindNoDeviceConnectedSubHeader();
  123. EXPECT_TRUE(no_device_connected_sub_header);
  124. EXPECT_EQ(0u, device_list()->GetIndexOf(no_device_connected_sub_header));
  125. EXPECT_EQ(1u, device_list()->children().size());
  126. }
  127. void CheckNotifyDeviceListChangedCount(size_t call_count) {
  128. EXPECT_EQ(call_count, fake_bluetooth_detailed_view()
  129. ->notify_device_list_changed_call_count());
  130. }
  131. views::View* device_list() {
  132. return static_cast<BluetoothDetailedView*>(
  133. fake_bluetooth_detailed_view_.get())
  134. ->device_list();
  135. }
  136. BluetoothDeviceListController* bluetooth_device_list_controller() {
  137. return bluetooth_device_list_controller_impl_.get();
  138. }
  139. FakeBluetoothDetailedView* fake_bluetooth_detailed_view() {
  140. return fake_bluetooth_detailed_view_.get();
  141. }
  142. protected:
  143. const std::vector<PairedBluetoothDevicePropertiesPtr> empty_list_;
  144. private:
  145. const TriView* FindSubHeaderWithText(const std::u16string text) {
  146. for (const auto* view : device_list()->children()) {
  147. if (std::strcmp("TriView", view->GetClassName()))
  148. continue;
  149. const TriView* sub_header = static_cast<const TriView*>(view);
  150. if (GetSubHeaderText(sub_header) == text)
  151. return sub_header;
  152. }
  153. return nullptr;
  154. }
  155. base::test::ScopedFeatureList feature_list_;
  156. std::unique_ptr<FakeBluetoothDetailedView> fake_bluetooth_detailed_view_;
  157. std::unique_ptr<BluetoothDeviceListControllerImpl>
  158. bluetooth_device_list_controller_impl_;
  159. };
  160. TEST_F(BluetoothDeviceListControllerTest,
  161. HasCorrectSubHeaderWithNoPairedDevices) {
  162. CheckNotifyDeviceListChangedCount(/*call_count=*/0u);
  163. bluetooth_device_list_controller()->UpdateBluetoothEnabledState(true);
  164. bluetooth_device_list_controller()->UpdateDeviceList(
  165. /*connected=*/empty_list_,
  166. /*previously_connected=*/empty_list_);
  167. CheckNotifyDeviceListChangedCount(/*call_count=*/1u);
  168. EXPECT_EQ(1u, device_list()->children().size());
  169. const TriView* no_device_connected_sub_header =
  170. FindNoDeviceConnectedSubHeader();
  171. EXPECT_TRUE(no_device_connected_sub_header);
  172. }
  173. TEST_F(BluetoothDeviceListControllerTest,
  174. HasCorrectDeviceListOrderWithPairedDevices) {
  175. CheckNotifyDeviceListChangedCount(/*call_count=*/0u);
  176. bluetooth_device_list_controller()->UpdateBluetoothEnabledState(true);
  177. std::vector<PairedBluetoothDevicePropertiesPtr> connected_list;
  178. connected_list.push_back(BuildDeviceProperties(kDeviceId1));
  179. bluetooth_device_list_controller()->UpdateDeviceList(
  180. /*connected=*/connected_list,
  181. /*previously_connected=*/empty_list_);
  182. CheckNotifyDeviceListChangedCount(/*call_count=*/1u);
  183. const TriView* connected_devices_sub_header = FindConnectedSubHeader();
  184. EXPECT_EQ(2u, device_list()->children().size());
  185. EXPECT_STREQ(kDeviceId1, GetDeviceId(GetFirstDeviceView()));
  186. EXPECT_TRUE(connected_devices_sub_header);
  187. CheckDeviceListOrdering(
  188. /*connected_device_count=*/connected_list.size(),
  189. /*previously_connected_device_count=*/empty_list_.size());
  190. std::vector<PairedBluetoothDevicePropertiesPtr> previously_connected_list;
  191. previously_connected_list.push_back(BuildDeviceProperties(kDeviceId2));
  192. bluetooth_device_list_controller()->UpdateDeviceList(
  193. /*connected=*/empty_list_,
  194. /*previously_connected=*/previously_connected_list);
  195. CheckNotifyDeviceListChangedCount(/*call_count=*/2u);
  196. const TriView* previously_connected_devices_sub_header =
  197. FindPreviouslyConnectedSubHeader();
  198. EXPECT_EQ(2u, device_list()->children().size());
  199. EXPECT_STREQ(kDeviceId2, GetDeviceId(GetFirstDeviceView()));
  200. EXPECT_TRUE(previously_connected_devices_sub_header);
  201. CheckDeviceListOrdering(
  202. /*connected_device_count=*/0,
  203. /*previously_connected_device_count=*/previously_connected_list.size());
  204. // "Update" the device list multiple times to be sure that no children are
  205. // duplicated and every child is re-ordered correctly.
  206. for (int i = 0; i < 2; i++) {
  207. bluetooth_device_list_controller()->UpdateDeviceList(
  208. /*connected=*/connected_list,
  209. /*previously_connected=*/previously_connected_list);
  210. }
  211. CheckNotifyDeviceListChangedCount(/*call_count=*/4u);
  212. EXPECT_EQ(5u, device_list()->children().size());
  213. CheckDeviceListOrdering(
  214. /*connected_device_count=*/connected_list.size(),
  215. /*previously_connected_device_count=*/previously_connected_list.size());
  216. }
  217. TEST_F(BluetoothDeviceListControllerTest, ExistingDeviceViewsAreUpdated) {
  218. CheckNotifyDeviceListChangedCount(/*call_count=*/0u);
  219. bluetooth_device_list_controller()->UpdateBluetoothEnabledState(true);
  220. std::vector<PairedBluetoothDevicePropertiesPtr> connected_list;
  221. connected_list.push_back(BuildDeviceProperties(kDeviceId1));
  222. bluetooth_device_list_controller()->UpdateDeviceList(
  223. /*connected=*/connected_list,
  224. /*previously_connected=*/empty_list_);
  225. CheckNotifyDeviceListChangedCount(/*call_count=*/1u);
  226. EXPECT_EQ(2u, device_list()->children().size());
  227. const BluetoothDeviceListItemView* first_item = GetFirstDeviceView();
  228. EXPECT_FALSE(first_item->device_properties()->nickname.has_value());
  229. connected_list.at(0)->nickname = kDeviceNickname;
  230. bluetooth_device_list_controller()->UpdateDeviceList(
  231. /*connected=*/connected_list,
  232. /*previously_connected=*/empty_list_);
  233. CheckNotifyDeviceListChangedCount(/*call_count=*/2u);
  234. EXPECT_EQ(2u, device_list()->children().size());
  235. EXPECT_EQ(1u, device_list()->GetIndexOf(first_item));
  236. EXPECT_TRUE(first_item->device_properties()->nickname.has_value());
  237. EXPECT_STREQ(kDeviceNickname,
  238. first_item->device_properties()->nickname.value().c_str());
  239. }
  240. TEST_F(BluetoothDeviceListControllerTest,
  241. DeviceListIsClearedWhenBluetoothBecomesDisabled) {
  242. CheckNotifyDeviceListChangedCount(/*call_count=*/0u);
  243. bluetooth_device_list_controller()->UpdateBluetoothEnabledState(true);
  244. std::vector<PairedBluetoothDevicePropertiesPtr> connected_list;
  245. connected_list.push_back(BuildDeviceProperties(kDeviceId1));
  246. bluetooth_device_list_controller()->UpdateDeviceList(
  247. /*connected=*/connected_list,
  248. /*previously_connected=*/empty_list_);
  249. CheckNotifyDeviceListChangedCount(/*call_count=*/1u);
  250. EXPECT_EQ(2u, device_list()->children().size());
  251. bluetooth_device_list_controller()->UpdateBluetoothEnabledState(false);
  252. EXPECT_EQ(0u, device_list()->children().size());
  253. }
  254. } // namespace ash