1// Copyright 2016 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
5#import "ios/chrome/browser/ui/voice/text_to_speech_player.h"
6
7#import <UIKit/UIKit.h>
8
9#include "base/mac/foundation_util.h"
10#import "base/test/ios/wait_util.h"
11#include "base/time/time.h"
12#import "ios/chrome/browser/ui/voice/voice_search_notification_names.h"
13#include "ios/web/public/test/web_task_environment.h"
14#include "testing/gtest/include/gtest/gtest.h"
15#include "testing/gtest_mac.h"
16#include "testing/platform_test.h"
17#include "url/gurl.h"
18
19#if !defined(__has_feature) || !__has_feature(objc_arc)
20#error "This file requires ARC support."
21#endif
22
23#pragma mark - TTSPlayerObserver
24
25// Test object that listens for TTS notifications.
26@interface TTSPlayerObserver : NSObject
27
28// The TextToSpeechPlayer passed on initialization.
29@property(nonatomic, strong) TextToSpeechPlayer* player;
30
31// Whether notifications have been received.
32@property(nonatomic, readonly) BOOL readyNotificationReceived;
33@property(nonatomic, readonly) BOOL willStartNotificationReceived;
34@property(nonatomic, readonly) BOOL didStopNotificationReceived;
35
36// Notification handlers.
37- (void)handleReadyNotification:(NSNotification*)notification;
38- (void)handleWillStartNotification:(NSNotification*)notification;
39- (void)handleDidStopNotification:(NSNotification*)notification;
40
41@end
42
43@implementation TTSPlayerObserver
44@synthesize player = _player;
45@synthesize readyNotificationReceived = _readyNotificationReceived;
46@synthesize willStartNotificationReceived = _willStartNotificationReceived;
47@synthesize didStopNotificationReceived = _didStopNotificationReceived;
48
49- (void)setPlayer:(TextToSpeechPlayer*)player {
50  NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
51  [defaultCenter removeObserver:self];
52  _player = player;
53  if (player) {
54    [defaultCenter addObserver:self
55                      selector:@selector(handleReadyNotification:)
56                          name:kTTSAudioReadyForPlaybackNotification
57                        object:player];
58    [defaultCenter addObserver:self
59                      selector:@selector(handleWillStartNotification:)
60                          name:kTTSWillStartPlayingNotification
61                        object:player];
62    [defaultCenter addObserver:self
63                      selector:@selector(handleDidStopNotification:)
64                          name:kTTSDidStopPlayingNotification
65                        object:player];
66  }
67}
68
69- (TextToSpeechPlayer*)player {
70  return _player;
71}
72
73- (void)handleReadyNotification:(NSNotification*)notification {
74  ASSERT_EQ(notification.object, self.player);
75  _readyNotificationReceived = YES;
76}
77
78- (void)handleWillStartNotification:(NSNotification*)notification {
79  ASSERT_EQ(notification.object, self.player);
80  _willStartNotificationReceived = YES;
81}
82
83- (void)handleDidStopNotification:(NSNotification*)notification {
84  ASSERT_EQ(notification.object, self.player);
85  _didStopNotificationReceived = YES;
86}
87
88@end
89
90#pragma mark - TextToSpeechPlayerTest
91
92class TextToSpeechPlayerTest : public PlatformTest {
93 protected:
94  void SetUp() override {
95    tts_player_ = [[TextToSpeechPlayer alloc] init];
96    tts_player_observer_ = [[TTSPlayerObserver alloc] init];
97    [tts_player_observer_ setPlayer:tts_player_];
98  }
99
100  TextToSpeechPlayer* tts_player_;
101  TTSPlayerObserver* tts_player_observer_;
102  web::WebTaskEnvironment task_environment_;
103};
104
105// Tests that kTTSAudioReadyForPlaybackNotification is received and that
106// TTSPlayer state is updated.
107TEST_F(TextToSpeechPlayerTest, ReadyForPlayback) {
108  NSData* audio_data = [@"audio_data" dataUsingEncoding:NSUTF8StringEncoding];
109  [tts_player_ prepareToPlayAudioData:audio_data];
110  EXPECT_TRUE([tts_player_observer_ readyNotificationReceived]);
111  EXPECT_TRUE([tts_player_ isReadyForPlayback]);
112}
113
114// Tests that kTTSAudioReadyForPlaybackNotification is received and that
115// TTSPlayer's |-readyForPlayback| is NO for empty data.
116TEST_F(TextToSpeechPlayerTest, ReadyForPlaybackEmtpyData) {
117  NSData* audio_data = [@"" dataUsingEncoding:NSUTF8StringEncoding];
118  [tts_player_ prepareToPlayAudioData:audio_data];
119  EXPECT_TRUE([tts_player_observer_ readyNotificationReceived]);
120  EXPECT_FALSE([tts_player_ isReadyForPlayback]);
121}
122
123// Tests that kTTSWillStartPlayingNotification is received when playback begins
124// and kTTSDidStopPlayingNotification is received when it is cancelled.
125// TODO(rohitrao): Disabled because the bots do not have a valid sound output
126// device.
127TEST_F(TextToSpeechPlayerTest, DISABLED_ValidPlaybackNotifications) {
128  NSString* path =
129      [[NSBundle mainBundle] pathForResource:@"test_sound"
130                                      ofType:@"m4a"
131                                 inDirectory:@"ios/chrome/test/data/voice"];
132  NSData* audio_data = [[NSData alloc] initWithContentsOfFile:path];
133  [tts_player_ prepareToPlayAudioData:audio_data];
134  [tts_player_ beginPlayback];
135  EXPECT_TRUE([tts_player_observer_ willStartNotificationReceived]);
136  EXPECT_TRUE([tts_player_ isPlayingAudio]);
137  [tts_player_ cancelPlayback];
138  EXPECT_TRUE([tts_player_observer_ didStopNotificationReceived]);
139  EXPECT_FALSE([tts_player_ isPlayingAudio]);
140}
141
142// Tests that playback is cancelled when the application enters the background
143// while playback is occurring.
144// TODO(rohitrao): Disabled because the bots do not have a valid sound output
145// device.
146TEST_F(TextToSpeechPlayerTest, DISABLED_BackgroundNotification) {
147  NSString* path =
148      [[NSBundle mainBundle] pathForResource:@"test_sound"
149                                      ofType:@"m4a"
150                                 inDirectory:@"ios/chrome/test/data/voice"];
151  NSData* audio_data = [[NSData alloc] initWithContentsOfFile:path];
152  [tts_player_ prepareToPlayAudioData:audio_data];
153  [tts_player_ beginPlayback];
154  EXPECT_TRUE([tts_player_observer_ willStartNotificationReceived]);
155  EXPECT_TRUE([tts_player_ isPlayingAudio]);
156  [[NSNotificationCenter defaultCenter]
157      postNotificationName:UIApplicationDidEnterBackgroundNotification
158                    object:[UIApplication sharedApplication]];
159  EXPECT_TRUE([tts_player_observer_ didStopNotificationReceived]);
160  EXPECT_FALSE([tts_player_ isPlayingAudio]);
161}
162