chromium/ios/chrome/browser/voice/model/text_to_speech_listener_unittest.mm

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/voice/model/text_to_speech_listener.h"

#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/web/public/test/fakes/fake_web_client.h"
#import "ios/web/public/test/scoped_testing_web_client.h"
#import "ios/web/public/test/web_state_test_util.h"
#import "ios/web/public/test/web_task_environment.h"
#import "ios/web/public/test/web_view_content_test_util.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"

namespace {
const char kExpectedDecodedData[] = "testaudo32oio";
NSString* const kHTMLFormat =
    @"<html><head><script>%@</script></head><body></body></html>";
NSString* const kValidVoiceSearchScript =
    @"(function(){var _a_tts='dGVzdGF1ZG8zMm9pbw==';var _m_tts= {}})";
}  // namespace

#pragma mark - TestTTSListenerDelegate

@interface TestTTSListenerDelegate : NSObject <TextToSpeechListenerDelegate> {
  // Backing objects for properties of the same name.
  NSData* _expectedAudioData;
}

// The expected audio data to be returned by the TextToSpeechListener.
@property(nonatomic, strong) NSData* expectedAudioData;

// Whether `-textToSpeechListener:didReceiveResult:` was called.
@property(nonatomic, assign) BOOL audioDataReceived;

@end

@implementation TestTTSListenerDelegate

@synthesize audioDataReceived = _audioDataReceived;

- (void)setExpectedAudioData:(NSData*)expectedAudioData {
  _expectedAudioData = expectedAudioData;
}

- (NSData*)expectedAudioData {
  return _expectedAudioData;
}

- (void)textToSpeechListener:(TextToSpeechListener*)listener
            didReceiveResult:(NSData*)result {
  EXPECT_NSEQ(self.expectedAudioData, result);
  self.audioDataReceived = YES;
}

- (void)textToSpeechListenerWebStateWasDestroyed:
    (TextToSpeechListener*)listener {
}

- (BOOL)shouldTextToSpeechListener:(TextToSpeechListener*)listener
                  parseDataFromURL:(const GURL&)URL {
  return YES;
}

@end

#pragma mark - TextToSpeechListenerTest

class TextToSpeechListenerTest : public PlatformTest {
 public:
  TextToSpeechListenerTest()
      : web_client_(std::make_unique<web::FakeWebClient>()) {}

  void SetUp() override {
    PlatformTest::SetUp();

    browser_state_ = TestChromeBrowserState::Builder().Build();

    web::WebState::CreateParams params(browser_state_.get());
    web_state_ = web::WebState::Create(params);
    web_state_->GetView();
    web_state_->SetKeepRenderProcessAlive(true);
    // Load Html is triggering several callbacks when called the first time.
    // Call it a first time before setting the `delegate_` to make sure that it
    // is working as expected in tests.
    web::test::LoadHtml(@"<html><body>Page loaded</body></html>", web_state());
    ASSERT_TRUE(
        web::test::WaitForWebViewContainingText(web_state(), "Page loaded"));

    delegate_ = [[TestTTSListenerDelegate alloc] init];
    listener_ = [[TextToSpeechListener alloc] initWithWebState:web_state()
                                                      delegate:delegate_];
  }

  void TestExtraction(NSString* html, NSData* expected_audio_data) {
    [delegate_ setExpectedAudioData:expected_audio_data];
    web::test::LoadHtml(html, web_state());
    ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
        base::Seconds(1000), true, ^bool {
          return [delegate_ audioDataReceived];
        }));
  }

 private:
  web::WebState* web_state() { return web_state_.get(); }

  web::ScopedTestingWebClient web_client_;
  web::WebTaskEnvironment task_environment_;
  std::unique_ptr<TestChromeBrowserState> browser_state_;
  std::unique_ptr<web::WebState> web_state_;

  TestTTSListenerDelegate* delegate_;
  TextToSpeechListener* listener_;
};

TEST_F(TextToSpeechListenerTest, ValidAudioDataTest) {
  NSData* expected_audio_data =
      [NSData dataWithBytes:&kExpectedDecodedData[0]
                     length:sizeof(kExpectedDecodedData) - 1];
  TestExtraction(
      [NSString stringWithFormat:kHTMLFormat, kValidVoiceSearchScript],
      expected_audio_data);
}

TEST_F(TextToSpeechListenerTest, InvalidAudioDataTest) {
  NSData* expected_audio_data = nil;
  TestExtraction([NSString stringWithFormat:kHTMLFormat, @""],
                 expected_audio_data);
}