1 // Copyright 2014 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.
5 #include "chrome/browser/copresence/chrome_whispernet_client.h"
11 #include "base/bind.h"
12 #include "base/macros.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/run_loop.h"
15 #include "base/stl_util.h"
16 #include "chrome/browser/extensions/api/copresence/copresence_api.h"
17 #include "chrome/browser/extensions/extension_browsertest.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/test/base/in_process_browser_test.h"
21 #include "components/audio_modem/public/audio_modem_types.h"
22 #include "components/audio_modem/public/whispernet_client.h"
23 #include "media/audio/audio_manager.h"
24 #include "media/audio/audio_manager_base.h"
25 #include "media/audio/audio_parameters.h"
26 #include "media/base/audio_bus.h"
27 #include "media/base/audio_converter.h"
29 using audio_modem::WhispernetClient
;
30 using audio_modem::AUDIBLE
;
31 using audio_modem::INAUDIBLE
;
32 using audio_modem::TokenParameters
;
36 // TODO(ckehoe): Use randomly generated tokens instead.
37 const char kSixZeros
[] = "MDAwMDAw";
38 const char kEightZeros
[] = "MDAwMDAwMDA";
39 const char kNineZeros
[] = "MDAwMDAwMDAw";
41 const size_t kTokenLengths
[2] = {6, 6};
43 // Copied from src/components/copresence/mediums/audio/audio_recorder.cc
44 std::string
AudioBusToString(scoped_refptr
<media::AudioBusRefCounted
> source
) {
46 buffer
.resize(source
->frames() * source
->channels() * sizeof(float));
47 float* buffer_view
= reinterpret_cast<float*>(string_as_array(&buffer
));
49 const int channels
= source
->channels();
50 for (int ch
= 0; ch
< channels
; ++ch
) {
51 for (int si
= 0, di
= ch
; si
< source
->frames(); ++si
, di
+= channels
)
52 buffer_view
[di
] = source
->channel(ch
)[si
];
58 void GetTokenParamsForLengths(const size_t token_lengths
[2],
59 TokenParameters
* params
) {
60 params
[0].length
= token_lengths
[0];
61 params
[1].length
= token_lengths
[1];
64 void IgnoreResult(bool success
) {}
68 class ChromeWhispernetClientTest
: public ExtensionBrowserTest
,
69 public media::AudioConverter::InputCallback
{
71 ChromeWhispernetClientTest()
72 : initialized_(false),
73 expected_audible_(false),
74 saved_samples_index_(0) {}
76 ~ChromeWhispernetClientTest() override
{}
78 void InitializeWhispernet() {
79 scoped_ptr
<WhispernetClient
> client(
80 new ChromeWhispernetClient(browser()->profile()));
81 client
->Initialize(base::Bind(
82 &ChromeWhispernetClientTest::InitCallback
, base::Unretained(this)));
84 run_loop_
.reset(new base::RunLoop());
86 EXPECT_TRUE(initialized_
);
89 // This needs to be called before any of the decoder tests are run. We can't
90 // run this code in the constructor or the SetUp methods because the audio
91 // manager seems to get initialized only *after* ExtensionBrowserTest::SetUp
92 // has finished executing. Setting up a testing AudioMager causes the actual
93 // create happening later in the browser initialization to fail. The only way
94 // around this at the moment seems to be to have this method called from
95 // every test before they try to decode.
97 // We get default parameters here instead of the constructor since
98 // initializing Whispernet also creates our AudioManager. Initializing from
99 // the test instead causes issues.
100 default_params_
= media::AudioManager::Get()->GetInputStreamParameters(
101 media::AudioManagerBase::kDefaultDeviceId
);
103 coder_params_
= media::AudioParameters(
104 default_params_
.format(), audio_modem::kDefaultChannelLayout
,
105 audio_modem::kDefaultSampleRate
, audio_modem::kDefaultBitsPerSample
,
106 default_params_
.frames_per_buffer(),
107 media::AudioParameters::NO_EFFECTS
);
109 converter_
.reset(new media::AudioConverter(
110 coder_params_
, default_params_
,
111 default_params_
.sample_rate() == coder_params_
.sample_rate()));
112 converter_
->AddInput(this);
115 void EncodeTokenAndSaveSamples(WhispernetClient
* client
,
117 const std::string
& token
,
118 const TokenParameters token_params
[2]) {
119 run_loop_
.reset(new base::RunLoop());
120 client
->RegisterSamplesCallback(
121 base::Bind(&ChromeWhispernetClientTest::SamplesCallback
,
122 base::Unretained(this)));
123 expected_token_
= token
;
124 expected_audible_
= audible
;
126 client
->EncodeToken(token
, audible
? AUDIBLE
: INAUDIBLE
, token_params
);
129 EXPECT_GT(saved_samples_
->frames(), 0);
132 void DecodeSamplesAndVerifyToken(WhispernetClient
* client
,
134 const std::string
& expected_token
,
135 const TokenParameters token_params
[2]) {
136 run_loop_
.reset(new base::RunLoop());
137 client
->RegisterTokensCallback(base::Bind(
138 &ChromeWhispernetClientTest::TokensCallback
, base::Unretained(this)));
139 expected_token_
= expected_token
;
140 expected_audible_
= expect_audible
;
142 ASSERT_GT(saved_samples_
->frames(), 0);
144 scoped_refptr
<media::AudioBusRefCounted
> samples_bus
=
145 ConvertSavedSamplesToSystemParams();
146 client
->DecodeSamples(expect_audible
? AUDIBLE
: INAUDIBLE
,
147 AudioBusToString(samples_bus
), token_params
);
151 void InitCallback(bool success
) {
152 EXPECT_TRUE(success
);
154 ASSERT_TRUE(run_loop_
);
158 void SamplesCallback(
159 audio_modem::AudioType type
,
160 const std::string
& token
,
161 const scoped_refptr
<media::AudioBusRefCounted
>& samples
) {
162 EXPECT_EQ(expected_token_
, token
);
163 EXPECT_EQ(expected_audible_
, type
== AUDIBLE
);
164 saved_samples_
= samples
;
165 ASSERT_TRUE(run_loop_
);
169 void TokensCallback(const std::vector
<audio_modem::AudioToken
>& tokens
) {
170 ASSERT_TRUE(run_loop_
);
173 EXPECT_EQ(expected_token_
, tokens
[0].token
);
174 EXPECT_EQ(expected_audible_
, tokens
[0].audible
);
178 scoped_refptr
<media::AudioBusRefCounted
> ConvertSavedSamplesToSystemParams() {
180 saved_samples_
->frames() *
181 std::ceil(static_cast<double>(default_params_
.sample_rate()) /
182 coder_params_
.sample_rate());
184 std::ceil(static_cast<double>(new_size
) / converter_
->ChunkSize()) *
185 converter_
->ChunkSize();
187 scoped_refptr
<media::AudioBusRefCounted
> converted_samples
=
188 media::AudioBusRefCounted::Create(default_params_
.channels(), new_size
);
190 // Convert our single channel samples to two channel. Decode samples
191 // expects 2 channel data.
192 saved_samples_stereo_
=
193 media::AudioBusRefCounted::Create(2, saved_samples_
->frames());
194 memcpy(saved_samples_stereo_
->channel(0), saved_samples_
->channel(0),
195 sizeof(float) * saved_samples_
->frames());
196 memcpy(saved_samples_stereo_
->channel(1), saved_samples_
->channel(0),
197 sizeof(float) * saved_samples_
->frames());
199 saved_samples_index_
= 0;
200 converter_
->Convert(converted_samples
.get());
202 return converted_samples
;
205 // AudioConverter::InputCallback overrides:
206 double ProvideInput(media::AudioBus
* dest
,
207 base::TimeDelta
/* buffer_delay */) override
{
208 // Copy any saved samples we have to the output bus.
209 const int remaining_frames
=
210 saved_samples_
->frames() - saved_samples_index_
;
211 const int frames_to_copy
= std::min(remaining_frames
, dest
->frames());
212 saved_samples_stereo_
->CopyPartialFramesTo(saved_samples_index_
,
213 frames_to_copy
, 0, dest
);
214 saved_samples_index_
+= frames_to_copy
;
216 // Pad any remaining space with zeroes.
217 if (remaining_frames
< dest
->frames()) {
218 dest
->ZeroFramesPartial(remaining_frames
,
219 dest
->frames() - remaining_frames
);
222 // Return the volume level.
226 scoped_ptr
<base::RunLoop
> run_loop_
;
229 std::string expected_token_
;
230 bool expected_audible_
;
232 scoped_refptr
<media::AudioBusRefCounted
> saved_samples_
;
233 scoped_refptr
<media::AudioBusRefCounted
> saved_samples_stereo_
;
234 int saved_samples_index_
;
236 scoped_ptr
<media::AudioConverter
> converter_
;
238 media::AudioParameters default_params_
;
239 media::AudioParameters coder_params_
;
241 DISALLOW_COPY_AND_ASSIGN(ChromeWhispernetClientTest
);
244 // These tests are irrelevant if NACL is disabled. See crbug.com/449198.
245 #if defined(DISABLE_NACL)
246 #define MAYBE_Initialize DISABLED_Initialize
247 #define MAYBE_EncodeAndDecode DISABLED_EncodeAndDecode
248 #define MAYBE_TokenLengths DISABLED_TokenLengths
249 #define MAYBE_Crc DISABLED_Crc
250 #define MAYBE_Parity DISABLED_Parity
252 #define MAYBE_Initialize Initialize
253 #define MAYBE_EncodeAndDecode EncodeAndDecode
254 #define MAYBE_TokenLengths TokenLengths
255 #define MAYBE_Crc Crc
256 #define MAYBE_Parity Parity
259 // This test trips up ASAN on ChromeOS. See:
260 // https://code.google.com/p/address-sanitizer/issues/detail?id=189
261 #if defined(DISABLE_NACL) || defined(OS_CHROMEOS)
262 #define MAYBE_MultipleClients DISABLED_MultipleClients
264 #define MAYBE_MultipleClients MultipleClients
267 IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest
, MAYBE_Initialize
) {
268 InitializeWhispernet();
271 IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest
, MAYBE_EncodeAndDecode
) {
272 scoped_ptr
<WhispernetClient
> client(
273 new ChromeWhispernetClient(browser()->profile()));
274 client
->Initialize(base::Bind(&IgnoreResult
));
277 TokenParameters token_params
[2];
278 GetTokenParamsForLengths(kTokenLengths
, token_params
);
280 EncodeTokenAndSaveSamples(client
.get(), true, kSixZeros
, token_params
);
281 DecodeSamplesAndVerifyToken(client
.get(), true, kSixZeros
, token_params
);
283 EncodeTokenAndSaveSamples(client
.get(), false, kSixZeros
, token_params
);
284 DecodeSamplesAndVerifyToken(client
.get(), false, kSixZeros
, token_params
);
287 IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest
, MAYBE_TokenLengths
) {
288 scoped_ptr
<WhispernetClient
> client(
289 new ChromeWhispernetClient(browser()->profile()));
290 client
->Initialize(base::Bind(&IgnoreResult
));
293 const size_t kLongTokenLengths
[2] = {8, 9};
294 TokenParameters token_params
[2];
295 GetTokenParamsForLengths(kLongTokenLengths
, token_params
);
297 EncodeTokenAndSaveSamples(client
.get(), true, kEightZeros
, token_params
);
298 DecodeSamplesAndVerifyToken(client
.get(), true, kEightZeros
, token_params
);
300 EncodeTokenAndSaveSamples(client
.get(), false, kNineZeros
, token_params
);
301 DecodeSamplesAndVerifyToken(client
.get(), false, kNineZeros
, token_params
);
304 IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest
, MAYBE_Crc
) {
305 scoped_ptr
<WhispernetClient
> client(
306 new ChromeWhispernetClient(browser()->profile()));
307 client
->Initialize(base::Bind(&IgnoreResult
));
310 TokenParameters token_params
[2];
311 GetTokenParamsForLengths(kTokenLengths
, token_params
);
312 token_params
[0].crc
= true;
313 token_params
[1].crc
= true;
315 EncodeTokenAndSaveSamples(client
.get(), true, kSixZeros
, token_params
);
316 DecodeSamplesAndVerifyToken(client
.get(), true, kSixZeros
, token_params
);
318 EncodeTokenAndSaveSamples(client
.get(), false, kSixZeros
, token_params
);
319 DecodeSamplesAndVerifyToken(client
.get(), false, kSixZeros
, token_params
);
322 IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest
, MAYBE_Parity
) {
323 scoped_ptr
<WhispernetClient
> client(
324 new ChromeWhispernetClient(browser()->profile()));
325 client
->Initialize(base::Bind(&IgnoreResult
));
328 TokenParameters token_params
[2];
329 GetTokenParamsForLengths(kTokenLengths
, token_params
);
330 token_params
[0].parity
= false;
331 token_params
[1].parity
= false;
333 EncodeTokenAndSaveSamples(client
.get(), true, kSixZeros
, token_params
);
334 DecodeSamplesAndVerifyToken(client
.get(), true, kSixZeros
, token_params
);
336 EncodeTokenAndSaveSamples(client
.get(), false, kSixZeros
, token_params
);
337 DecodeSamplesAndVerifyToken(client
.get(), false, kSixZeros
, token_params
);
340 IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest
, MAYBE_MultipleClients
) {
341 scoped_ptr
<WhispernetClient
> client_1(
342 new ChromeWhispernetClient(browser()->profile()));
343 scoped_ptr
<WhispernetClient
> client_2(
344 new ChromeWhispernetClient(browser()->profile()));
345 scoped_ptr
<WhispernetClient
> client_3(
346 new ChromeWhispernetClient(browser()->profile()));
349 TokenParameters token_params
[2];
350 GetTokenParamsForLengths(kTokenLengths
, token_params
);
352 // Test concurrent initialization.
353 client_1
->Initialize(base::Bind(&IgnoreResult
));
354 client_2
->Initialize(base::Bind(&IgnoreResult
));
356 EncodeTokenAndSaveSamples(client_1
.get(), true, kSixZeros
, token_params
);
357 DecodeSamplesAndVerifyToken(client_1
.get(), true, kSixZeros
, token_params
);
359 EncodeTokenAndSaveSamples(client_2
.get(), false, kSixZeros
, token_params
);
360 DecodeSamplesAndVerifyToken(client_2
.get(), false, kSixZeros
, token_params
);
362 // Test sequential initialization.
363 client_3
->Initialize(base::Bind(&IgnoreResult
));
365 EncodeTokenAndSaveSamples(client_3
.get(), true, kSixZeros
, token_params
);
366 DecodeSamplesAndVerifyToken(client_3
.get(), true, kSixZeros
, token_params
);
368 const size_t kLongTokenLengths
[2] = {8, 9};
369 GetTokenParamsForLengths(kLongTokenLengths
, token_params
);
371 EncodeTokenAndSaveSamples(client_2
.get(), true, kEightZeros
, token_params
);
372 DecodeSamplesAndVerifyToken(client_2
.get(), true, kEightZeros
, token_params
);