unified_audio_detailed_view_controller_unittest.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. // Copyright (c) 2020 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/audio/unified_audio_detailed_view_controller.h"
  5. #include "ash/accessibility/accessibility_controller_impl.h"
  6. #include "ash/constants/ash_features.h"
  7. #include "ash/session/session_controller_impl.h"
  8. #include "ash/shell.h"
  9. #include "ash/system/audio/audio_detailed_view.h"
  10. #include "ash/system/audio/mic_gain_slider_controller.h"
  11. #include "ash/system/tray/hover_highlight_view.h"
  12. #include "ash/system/unified/unified_system_tray_controller.h"
  13. #include "ash/system/unified/unified_system_tray_model.h"
  14. #include "ash/test/ash_test_base.h"
  15. #include "base/bind.h"
  16. #include "base/memory/scoped_refptr.h"
  17. #include "base/test/scoped_feature_list.h"
  18. #include "chromeos/ash/components/audio/audio_devices_pref_handler.h"
  19. #include "chromeos/ash/components/audio/audio_devices_pref_handler_stub.h"
  20. #include "chromeos/ash/components/dbus/audio/cras_audio_client.h"
  21. #include "chromeos/ash/components/dbus/audio/fake_cras_audio_client.h"
  22. #include "components/live_caption/pref_names.h"
  23. #include "components/soda/soda_installer_impl_chromeos.h"
  24. #include "media/base/media_switches.h"
  25. #include "mojo/public/cpp/bindings/receiver_set.h"
  26. #include "testing/gmock/include/gmock/gmock.h"
  27. #include "third_party/cros_system_api/dbus/service_constants.h"
  28. #include "ui/events/base_event_utils.h"
  29. #include "ui/views/controls/label.h"
  30. #include "ui/views/test/button_test_api.h"
  31. #include "ui/views/widget/widget.h"
  32. namespace ash {
  33. namespace {
  34. constexpr uint64_t kMicJackId = 10010;
  35. constexpr uint64_t kInternalMicId = 10003;
  36. constexpr uint64_t kFrontMicId = 10012;
  37. constexpr uint64_t kRearMicId = 10013;
  38. const std::u16string kInitialLiveCaptionViewSubtitleText = u"This is a test";
  39. const std::u16string kSodaDownloaded = u"Speech files downloaded";
  40. const std::u16string kSodaInProgress25 =
  41. u"Downloading speech recognition files… 25%";
  42. const std::u16string kSodaInProgress50 =
  43. u"Downloading speech recognition files… 50%";
  44. const std::u16string kSodaFailed =
  45. u"Can't download speech files. Try again later.";
  46. speech::LanguageCode en_us() {
  47. return speech::LanguageCode::kEnUs;
  48. }
  49. speech::LanguageCode fr_fr() {
  50. return speech::LanguageCode::kFrFr;
  51. }
  52. struct AudioNodeInfo {
  53. bool is_input;
  54. uint64_t id;
  55. const char* const device_name;
  56. const char* const type;
  57. const char* const name;
  58. const uint32_t audio_effect;
  59. };
  60. const uint32_t kInputMaxSupportedChannels = 1;
  61. const uint32_t kOutputMaxSupportedChannels = 2;
  62. const AudioNodeInfo kMicJack[] = {
  63. {true, kMicJackId, "Fake Mic Jack", "MIC", "Mic Jack", 0}};
  64. const AudioNodeInfo kInternalMic[] = {{true, kInternalMicId, "Fake Mic",
  65. "INTERNAL_MIC", "Internal Mic",
  66. cras::EFFECT_TYPE_NOISE_CANCELLATION}};
  67. const AudioNodeInfo kFrontMic[] = {
  68. {true, kFrontMicId, "Fake Front Mic", "FRONT_MIC", "Front Mic", 0}};
  69. const AudioNodeInfo kRearMic[] = {
  70. {true, kRearMicId, "Fake Rear Mic", "REAR_MIC", "Rear Mic", 0}};
  71. AudioNode GenerateAudioNode(const AudioNodeInfo* node_info) {
  72. uint64_t stable_device_id_v2 = 0;
  73. uint64_t stable_device_id_v1 = node_info->id;
  74. return AudioNode(node_info->is_input, node_info->id, false,
  75. stable_device_id_v1, stable_device_id_v2,
  76. node_info->device_name, node_info->type, node_info->name,
  77. false /* is_active*/, 0 /* pluged_time */,
  78. node_info->is_input ? kInputMaxSupportedChannels
  79. : kOutputMaxSupportedChannels,
  80. node_info->audio_effect);
  81. }
  82. AudioNodeList GenerateAudioNodeList(
  83. const std::vector<const AudioNodeInfo*>& nodes) {
  84. AudioNodeList node_list;
  85. for (auto* node_info : nodes) {
  86. node_list.push_back(GenerateAudioNode(node_info));
  87. }
  88. return node_list;
  89. }
  90. } // namespace
  91. // Test param is the version of stabel device id used by audio node.
  92. class UnifiedAudioDetailedViewControllerTest : public AshTestBase {
  93. public:
  94. UnifiedAudioDetailedViewControllerTest() {}
  95. ~UnifiedAudioDetailedViewControllerTest() override = default;
  96. // AshTestBase:
  97. void SetUp() override {
  98. AshTestBase::SetUp();
  99. audio_pref_handler_ = base::MakeRefCounted<AudioDevicesPrefHandlerStub>();
  100. cras_audio_handler_ = CrasAudioHandler::Get();
  101. cras_audio_handler_->SetPrefHandlerForTesting(audio_pref_handler_);
  102. tray_model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
  103. tray_controller_ =
  104. std::make_unique<UnifiedSystemTrayController>(tray_model_.get());
  105. audio_detailed_view_controller_ =
  106. std::make_unique<UnifiedAudioDetailedViewController>(
  107. tray_controller_.get());
  108. map_device_sliders_callback_ = base::BindRepeating(
  109. &UnifiedAudioDetailedViewControllerTest::AddViewToSliderDeviceMap,
  110. base::Unretained(this));
  111. MicGainSliderController::SetMapDeviceSliderCallbackForTest(
  112. &map_device_sliders_callback_);
  113. noise_cancellation_toggle_callback_ =
  114. base::BindRepeating(&UnifiedAudioDetailedViewControllerTest::
  115. AddViewToNoiseCancellationToggleMap,
  116. base::Unretained(this));
  117. AudioDetailedView::SetMapNoiseCancellationToggleCallbackForTest(
  118. &noise_cancellation_toggle_callback_);
  119. }
  120. void TearDown() override {
  121. MicGainSliderController::SetMapDeviceSliderCallbackForTest(nullptr);
  122. audio_pref_handler_ = nullptr;
  123. audio_detailed_view_ = nullptr;
  124. audio_detailed_view_.reset();
  125. audio_detailed_view_controller_.reset();
  126. tray_controller_.reset();
  127. tray_model_.reset();
  128. AshTestBase::TearDown();
  129. }
  130. void AddViewToSliderDeviceMap(uint64_t device_id, views::View* view) {
  131. sliders_map_[device_id] = view;
  132. }
  133. void AddViewToNoiseCancellationToggleMap(uint64_t device_id,
  134. views::View* view) {
  135. toggles_map_[device_id] = view;
  136. }
  137. void ToggleLiveCaption() {
  138. audio_detailed_view()->HandleViewClicked(live_caption_view());
  139. }
  140. protected:
  141. FakeCrasAudioClient* fake_cras_audio_client() {
  142. return FakeCrasAudioClient::Get();
  143. }
  144. AudioDetailedView* audio_detailed_view() {
  145. if (!audio_detailed_view_) {
  146. audio_detailed_view_ = base::WrapUnique(static_cast<AudioDetailedView*>(
  147. audio_detailed_view_controller_->CreateView()));
  148. }
  149. return audio_detailed_view_.get();
  150. }
  151. HoverHighlightView* live_caption_view() {
  152. return audio_detailed_view()->live_caption_view_;
  153. }
  154. bool live_caption_enabled() {
  155. return Shell::Get()->accessibility_controller()->live_caption().enabled();
  156. }
  157. std::map<uint64_t, views::View*> sliders_map_;
  158. std::map<uint64_t, views::View*> toggles_map_;
  159. MicGainSliderController::MapDeviceSliderCallback map_device_sliders_callback_;
  160. AudioDetailedView::NoiseCancellationCallback
  161. noise_cancellation_toggle_callback_;
  162. CrasAudioHandler* cras_audio_handler_ = nullptr; // Not owned.
  163. scoped_refptr<AudioDevicesPrefHandlerStub> audio_pref_handler_;
  164. std::unique_ptr<UnifiedAudioDetailedViewController>
  165. audio_detailed_view_controller_;
  166. scoped_refptr<UnifiedSystemTrayModel> tray_model_;
  167. std::unique_ptr<UnifiedSystemTrayController> tray_controller_;
  168. base::test::ScopedFeatureList scoped_feature_list_;
  169. std::unique_ptr<AudioDetailedView> audio_detailed_view_;
  170. };
  171. TEST_F(UnifiedAudioDetailedViewControllerTest, OnlyOneVisibleSlider) {
  172. std::unique_ptr<views::View> view =
  173. base::WrapUnique(audio_detailed_view_controller_->CreateView());
  174. fake_cras_audio_client()->SetAudioNodesAndNotifyObserversForTesting(
  175. GenerateAudioNodeList({kInternalMic, kMicJack}));
  176. // Only slider corresponding to the Internal Mic should be visible initially.
  177. cras_audio_handler_->SwitchToDevice(
  178. AudioDevice(GenerateAudioNode(kInternalMic)), true,
  179. CrasAudioHandler::ACTIVATE_BY_USER);
  180. EXPECT_EQ(kInternalMicId, cras_audio_handler_->GetPrimaryActiveInputNode());
  181. EXPECT_TRUE(sliders_map_.find(kInternalMicId)->second->GetVisible());
  182. EXPECT_FALSE(sliders_map_.find(kMicJackId)->second->GetVisible());
  183. // Switching to Mic Jack should flip the visibility of the sliders.
  184. cras_audio_handler_->SwitchToDevice(AudioDevice(GenerateAudioNode(kMicJack)),
  185. true, CrasAudioHandler::ACTIVATE_BY_USER);
  186. EXPECT_EQ(kMicJackId, cras_audio_handler_->GetPrimaryActiveInputNode());
  187. EXPECT_TRUE(sliders_map_.find(kMicJackId)->second->GetVisible());
  188. EXPECT_FALSE(sliders_map_.find(kInternalMicId)->second->GetVisible());
  189. }
  190. TEST_F(UnifiedAudioDetailedViewControllerTest,
  191. DualInternalMicHasSingleVisibleSlider) {
  192. fake_cras_audio_client()->SetAudioNodesAndNotifyObserversForTesting(
  193. GenerateAudioNodeList({kFrontMic, kRearMic}));
  194. // Verify the device has dual internal mics.
  195. EXPECT_TRUE(cras_audio_handler_->HasDualInternalMic());
  196. std::unique_ptr<views::View> view =
  197. base::WrapUnique(audio_detailed_view_controller_->CreateView());
  198. // Verify there is only 1 slider in the view.
  199. EXPECT_EQ(sliders_map_.size(), 1u);
  200. // Verify the slider is visible.
  201. EXPECT_TRUE(sliders_map_.begin()->second->GetVisible());
  202. }
  203. TEST_F(UnifiedAudioDetailedViewControllerTest,
  204. NoiseCancellationToggleNotDisplayedIfNotSupported) {
  205. fake_cras_audio_client()->SetAudioNodesAndNotifyObserversForTesting(
  206. GenerateAudioNodeList({kInternalMic, kMicJack, kFrontMic, kRearMic}));
  207. fake_cras_audio_client()->SetNoiseCancellationSupported(false);
  208. cras_audio_handler_->SwitchToDevice(
  209. AudioDevice(GenerateAudioNode(kInternalMic)), true,
  210. CrasAudioHandler::ACTIVATE_BY_USER);
  211. std::unique_ptr<views::View> view =
  212. base::WrapUnique(audio_detailed_view_controller_->CreateView());
  213. EXPECT_EQ(0u, toggles_map_.size());
  214. }
  215. TEST_F(UnifiedAudioDetailedViewControllerTest,
  216. NoiseCancellationToggleDisplayedIfSupportedAndInternal) {
  217. fake_cras_audio_client()->SetAudioNodesAndNotifyObserversForTesting(
  218. GenerateAudioNodeList({kInternalMic, kMicJack, kFrontMic, kRearMic}));
  219. fake_cras_audio_client()->SetNoiseCancellationSupported(true);
  220. cras_audio_handler_->RequestNoiseCancellationSupported(base::DoNothing());
  221. auto internal_mic = AudioDevice(GenerateAudioNode(kInternalMic));
  222. cras_audio_handler_->SwitchToDevice(internal_mic, true,
  223. CrasAudioHandler::ACTIVATE_BY_USER);
  224. std::unique_ptr<views::View> view =
  225. base::WrapUnique(audio_detailed_view_controller_->CreateView());
  226. EXPECT_EQ(1u, toggles_map_.size());
  227. views::ToggleButton* toggle =
  228. (views::ToggleButton*)toggles_map_[internal_mic.id]->children()[1];
  229. EXPECT_TRUE(toggle->GetIsOn());
  230. }
  231. TEST_F(UnifiedAudioDetailedViewControllerTest,
  232. NoiseCancellationToggleChangesPrefAndSendsDbusSignal) {
  233. audio_pref_handler_->SetNoiseCancellationState(false);
  234. fake_cras_audio_client()->SetAudioNodesAndNotifyObserversForTesting(
  235. GenerateAudioNodeList({kInternalMic, kMicJack, kFrontMic, kRearMic}));
  236. fake_cras_audio_client()->SetNoiseCancellationSupported(true);
  237. cras_audio_handler_->RequestNoiseCancellationSupported(base::DoNothing());
  238. auto internal_mic = AudioDevice(GenerateAudioNode(kInternalMic));
  239. cras_audio_handler_->SwitchToDevice(internal_mic, true,
  240. CrasAudioHandler::ACTIVATE_BY_USER);
  241. std::unique_ptr<views::View> view =
  242. base::WrapUnique(audio_detailed_view_controller_->CreateView());
  243. EXPECT_EQ(1u, toggles_map_.size());
  244. views::ToggleButton* toggle =
  245. (views::ToggleButton*)toggles_map_[internal_mic.id]->children()[1];
  246. auto widget = CreateFramelessTestWidget();
  247. widget->SetContentsView(toggle);
  248. // The toggle loaded the pref correctly.
  249. EXPECT_FALSE(toggle->GetIsOn());
  250. EXPECT_FALSE(audio_pref_handler_->GetNoiseCancellationState());
  251. ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
  252. ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
  253. ui::EF_NONE);
  254. // Flipping the toggle.
  255. views::test::ButtonTestApi(toggle).NotifyClick(press);
  256. // The new state of the toggle must be saved to the prefs.
  257. EXPECT_TRUE(audio_pref_handler_->GetNoiseCancellationState());
  258. // Flipping back and checking the prefs again.
  259. views::test::ButtonTestApi(toggle).NotifyClick(press);
  260. EXPECT_FALSE(audio_pref_handler_->GetNoiseCancellationState());
  261. }
  262. TEST_F(UnifiedAudioDetailedViewControllerTest,
  263. NoiseCancellationUpdatedWhenDeviceChanges) {
  264. fake_cras_audio_client()->SetAudioNodesAndNotifyObserversForTesting(
  265. GenerateAudioNodeList({kInternalMic, kMicJack, kFrontMic, kRearMic}));
  266. fake_cras_audio_client()->SetNoiseCancellationSupported(true);
  267. cras_audio_handler_->RequestNoiseCancellationSupported(base::DoNothing());
  268. cras_audio_handler_->SwitchToDevice(AudioDevice(GenerateAudioNode(kMicJack)),
  269. true, CrasAudioHandler::ACTIVATE_BY_USER);
  270. std::unique_ptr<views::View> view =
  271. base::WrapUnique(audio_detailed_view_controller_->CreateView());
  272. EXPECT_EQ(0u, toggles_map_.size());
  273. cras_audio_handler_->SwitchToDevice(
  274. AudioDevice(GenerateAudioNode(kInternalMic)), true,
  275. CrasAudioHandler::ACTIVATE_BY_USER);
  276. EXPECT_EQ(1u, toggles_map_.size());
  277. }
  278. TEST_F(UnifiedAudioDetailedViewControllerTest, ToggleLiveCaption) {
  279. scoped_feature_list_.InitWithFeatures(
  280. {media::kLiveCaption, media::kLiveCaptionSystemWideOnChromeOS,
  281. ash::features::kOnDeviceSpeechRecognition},
  282. {});
  283. EXPECT_TRUE(live_caption_view());
  284. EXPECT_FALSE(live_caption_enabled());
  285. ToggleLiveCaption();
  286. EXPECT_TRUE(live_caption_view());
  287. EXPECT_TRUE(live_caption_enabled());
  288. ToggleLiveCaption();
  289. EXPECT_TRUE(live_caption_view());
  290. EXPECT_FALSE(live_caption_enabled());
  291. }
  292. TEST_F(UnifiedAudioDetailedViewControllerTest, LiveCaptionNotAvailable) {
  293. // If the Live Caption feature flags are not set, the Live Caption toggle will
  294. // not appear in audio settings.
  295. EXPECT_FALSE(live_caption_view());
  296. EXPECT_FALSE(live_caption_enabled());
  297. }
  298. class UnifiedAudioDetailedViewControllerSodaTest
  299. : public UnifiedAudioDetailedViewControllerTest {
  300. protected:
  301. UnifiedAudioDetailedViewControllerSodaTest() = default;
  302. UnifiedAudioDetailedViewControllerSodaTest(
  303. const UnifiedAudioDetailedViewControllerSodaTest&) = delete;
  304. UnifiedAudioDetailedViewControllerSodaTest& operator=(
  305. const UnifiedAudioDetailedViewControllerSodaTest&) = delete;
  306. ~UnifiedAudioDetailedViewControllerSodaTest() override = default;
  307. void SetUp() override {
  308. UnifiedAudioDetailedViewControllerTest::SetUp();
  309. // Since this test suite is part of ash unit tests, the
  310. // SodaInstallerImplChromeOS is never created (it's normally created when
  311. // `ChromeBrowserMainPartsAsh` initializes). Create it here so that
  312. // calling speech::SodaInstaller::GetInstance() returns a valid instance.
  313. scoped_feature_list_.InitWithFeatures(
  314. {ash::features::kOnDeviceSpeechRecognition, media::kLiveCaption,
  315. media::kLiveCaptionMultiLanguage,
  316. media::kLiveCaptionSystemWideOnChromeOS},
  317. {});
  318. soda_installer_impl_ =
  319. std::make_unique<speech::SodaInstallerImplChromeOS>();
  320. EnableLiveCaption(true);
  321. SetLiveCaptionViewSubtitleText(kInitialLiveCaptionViewSubtitleText);
  322. SetLiveCaptionLocale("en-US");
  323. }
  324. void TearDown() override {
  325. soda_installer_impl_.reset();
  326. UnifiedAudioDetailedViewControllerTest::TearDown();
  327. }
  328. void EnableLiveCaption(bool enabled) {
  329. Shell::Get()->accessibility_controller()->live_caption().SetEnabled(
  330. enabled);
  331. }
  332. void SetLiveCaptionLocale(const std::string& locale) {
  333. Shell::Get()->session_controller()->GetActivePrefService()->SetString(
  334. ::prefs::kLiveCaptionLanguageCode, locale);
  335. }
  336. speech::SodaInstaller* soda_installer() {
  337. return speech::SodaInstaller::GetInstance();
  338. }
  339. void SetLiveCaptionViewSubtitleText(std::u16string text) {
  340. live_caption_view()->SetSubText(text);
  341. }
  342. std::u16string GetLiveCaptionViewSubtitleText() {
  343. return live_caption_view()->sub_text_label()->GetText();
  344. }
  345. private:
  346. std::unique_ptr<speech::SodaInstallerImplChromeOS> soda_installer_impl_;
  347. base::test::ScopedFeatureList scoped_feature_list_;
  348. };
  349. // Ensures that the Dictation subtitle changes when SODA AND the language pack
  350. // matching the Live Caption locale are installed.
  351. TEST_F(UnifiedAudioDetailedViewControllerSodaTest,
  352. OnSodaInstalledNotification) {
  353. SetLiveCaptionLocale("fr-FR");
  354. // Pretend that the SODA binary was installed. We still need to wait for the
  355. // correct language pack before doing anything.
  356. soda_installer()->NotifySodaInstalledForTesting();
  357. EXPECT_EQ(kInitialLiveCaptionViewSubtitleText,
  358. GetLiveCaptionViewSubtitleText());
  359. soda_installer()->NotifySodaInstalledForTesting(en_us());
  360. EXPECT_EQ(kInitialLiveCaptionViewSubtitleText,
  361. GetLiveCaptionViewSubtitleText());
  362. soda_installer()->NotifySodaInstalledForTesting(fr_fr());
  363. EXPECT_EQ(kSodaDownloaded, GetLiveCaptionViewSubtitleText());
  364. }
  365. // Ensures we only notify the user of progress for the language pack matching
  366. // the Live Caption locale.
  367. TEST_F(UnifiedAudioDetailedViewControllerSodaTest, OnSodaProgressNotification) {
  368. SetLiveCaptionLocale("en-US");
  369. soda_installer()->NotifySodaProgressForTesting(75, fr_fr());
  370. EXPECT_EQ(kInitialLiveCaptionViewSubtitleText,
  371. GetLiveCaptionViewSubtitleText());
  372. soda_installer()->NotifySodaProgressForTesting(50);
  373. EXPECT_EQ(kSodaInProgress50, GetLiveCaptionViewSubtitleText());
  374. soda_installer()->NotifySodaProgressForTesting(25, en_us());
  375. EXPECT_EQ(kSodaInProgress25, GetLiveCaptionViewSubtitleText());
  376. }
  377. // Ensures we notify the user of an error when the SODA binary fails to
  378. // download.
  379. TEST_F(UnifiedAudioDetailedViewControllerSodaTest,
  380. SodaBinaryErrorNotification) {
  381. soda_installer()->NotifySodaErrorForTesting();
  382. EXPECT_EQ(kSodaFailed, GetLiveCaptionViewSubtitleText());
  383. }
  384. // Ensures we only notify the user of an error if the failed language pack
  385. // matches the Live Caption locale.
  386. TEST_F(UnifiedAudioDetailedViewControllerSodaTest,
  387. SodaLanguageErrorNotification) {
  388. SetLiveCaptionLocale("en-US");
  389. soda_installer()->NotifySodaErrorForTesting(fr_fr());
  390. EXPECT_EQ(kInitialLiveCaptionViewSubtitleText,
  391. GetLiveCaptionViewSubtitleText());
  392. soda_installer()->NotifySodaErrorForTesting(en_us());
  393. EXPECT_EQ(kSodaFailed, GetLiveCaptionViewSubtitleText());
  394. }
  395. // Ensures that we don't respond to SODA download updates when Live Caption is
  396. // off.
  397. TEST_F(UnifiedAudioDetailedViewControllerSodaTest,
  398. SodaDownloadLiveCaptionDisabled) {
  399. EnableLiveCaption(false);
  400. EXPECT_EQ(kInitialLiveCaptionViewSubtitleText,
  401. GetLiveCaptionViewSubtitleText());
  402. soda_installer()->NotifySodaErrorForTesting();
  403. EXPECT_EQ(kInitialLiveCaptionViewSubtitleText,
  404. GetLiveCaptionViewSubtitleText());
  405. soda_installer()->NotifySodaInstalledForTesting();
  406. EXPECT_EQ(kInitialLiveCaptionViewSubtitleText,
  407. GetLiveCaptionViewSubtitleText());
  408. soda_installer()->NotifySodaProgressForTesting(50);
  409. EXPECT_EQ(kInitialLiveCaptionViewSubtitleText,
  410. GetLiveCaptionViewSubtitleText());
  411. }
  412. } // namespace ash