| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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 #include "chrome/browser/extensions/api/audio_modem/audio_modem_api.h" | |
| 6 | |
| 7 #include <map> | |
| 8 #include <memory> | |
| 9 #include <string> | |
| 10 #include <utility> | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/callback.h" | |
| 14 #include "base/memory/ptr_util.h" | |
| 15 #include "base/memory/ref_counted.h" | |
| 16 #include "base/values.h" | |
| 17 #include "chrome/browser/extensions/extension_api_unittest.h" | |
| 18 #include "chrome/browser/extensions/extension_function_test_utils.h" | |
| 19 #include "chrome/browser/extensions/test_extension_system.h" | |
| 20 #include "components/audio_modem/public/modem.h" | |
| 21 #include "components/audio_modem/test/stub_modem.h" | |
| 22 #include "components/audio_modem/test/stub_whispernet_client.h" | |
| 23 #include "extensions/browser/api_test_utils.h" | |
| 24 #include "extensions/browser/event_router.h" | |
| 25 #include "extensions/browser/event_router_factory.h" | |
| 26 | |
| 27 using audio_modem::AUDIBLE; | |
| 28 using audio_modem::AudioToken; | |
| 29 using audio_modem::INAUDIBLE; | |
| 30 using audio_modem::StubModem; | |
| 31 using audio_modem::StubWhispernetClient; | |
| 32 | |
| 33 using base::BinaryValue; | |
| 34 using base::DictionaryValue; | |
| 35 using base::ListValue; | |
| 36 using base::StringValue; | |
| 37 using base::Value; | |
| 38 | |
| 39 using content::BrowserContext; | |
| 40 | |
| 41 namespace ext_test_utils = extension_function_test_utils; | |
| 42 | |
| 43 namespace extensions { | |
| 44 | |
| 45 namespace { | |
| 46 | |
| 47 // The TestingFactoryFunction uses a BrowserContext as its context pointer. | |
| 48 // But each BrowserContext is still associated with a unit test. | |
| 49 // So we store the StubModem created in each test. | |
| 50 std::map<BrowserContext*, StubModem*> g_modems; | |
| 51 | |
| 52 // Create a test AudioModemAPI and store the modem it uses. | |
| 53 std::unique_ptr<KeyedService> ApiFactoryFunction(BrowserContext* context) { | |
| 54 StubModem* modem = new StubModem; | |
| 55 g_modems[context] = modem; | |
| 56 return base::WrapUnique(new AudioModemAPI( | |
| 57 context, | |
| 58 base::WrapUnique<audio_modem::WhispernetClient>(new StubWhispernetClient), | |
| 59 base::WrapUnique<audio_modem::Modem>(modem))); | |
| 60 } | |
| 61 | |
| 62 std::unique_ptr<DictionaryValue> CreateParams(const std::string& audio_band) { | |
| 63 std::unique_ptr<DictionaryValue> params(new DictionaryValue); | |
| 64 params->SetInteger("timeoutMillis", 60000); | |
| 65 params->SetString("band", audio_band); | |
| 66 params->SetInteger("encoding.tokenLength", 4); | |
| 67 return params; | |
| 68 } | |
| 69 | |
| 70 std::unique_ptr<BinaryValue> CreateToken(const std::string& token) { | |
| 71 return BinaryValue::CreateWithCopiedBuffer(token.c_str(), token.size()); | |
| 72 } | |
| 73 | |
| 74 std::unique_ptr<ListValue> CreateList(std::unique_ptr<Value> single_elt) { | |
| 75 std::unique_ptr<ListValue> list(new ListValue); | |
| 76 list->Append(std::move(single_elt)); | |
| 77 return list; | |
| 78 } | |
| 79 | |
| 80 std::unique_ptr<ListValue> CreateList(std::unique_ptr<Value> elt1, | |
| 81 std::unique_ptr<Value> elt2) { | |
| 82 std::unique_ptr<ListValue> list(new ListValue); | |
| 83 list->Append(std::move(elt1)); | |
| 84 list->Append(std::move(elt2)); | |
| 85 return list; | |
| 86 } | |
| 87 | |
| 88 DictionaryValue* CreateReceivedToken(const std::string& token, | |
| 89 const std::string& audio_band) { | |
| 90 DictionaryValue* out = new DictionaryValue; | |
| 91 out->Set("token", CreateToken(token)); | |
| 92 out->SetString("band", audio_band); | |
| 93 return out; | |
| 94 } | |
| 95 | |
| 96 bool ReceivedSingleToken(const Event* event, | |
| 97 const DictionaryValue* expected_token) { | |
| 98 ListValue* received_tokens; | |
| 99 event->event_args->GetList(0, &received_tokens); | |
| 100 if (received_tokens->GetSize() != 1) | |
| 101 return false; | |
| 102 | |
| 103 DictionaryValue* received_token; | |
| 104 received_tokens->GetDictionary(0, &received_token); | |
| 105 return received_token->Equals(expected_token); | |
| 106 } | |
| 107 | |
| 108 // TODO(ckehoe): Put this in //extensions/test. | |
| 109 // Then replace the other EventRouter mocks. | |
| 110 class StubEventRouter : public EventRouter { | |
| 111 public: | |
| 112 // Callback to receive events. First argument is | |
| 113 // the extension id to receive the event. | |
| 114 using EventCallback = | |
| 115 base::Callback<void(const std::string&, std::unique_ptr<Event>)>; | |
| 116 | |
| 117 explicit StubEventRouter(BrowserContext* context) | |
| 118 : EventRouter(context, nullptr) {} | |
| 119 | |
| 120 void DispatchEventToExtension(const std::string& extension_id, | |
| 121 std::unique_ptr<Event> event) override { | |
| 122 event_callback_.Run(extension_id, std::move(event)); | |
| 123 } | |
| 124 | |
| 125 void SetEventCallBack(EventCallback event_callback) { | |
| 126 event_callback_ = event_callback; | |
| 127 } | |
| 128 | |
| 129 void ClearEventCallback() { | |
| 130 event_callback_.Reset(); | |
| 131 } | |
| 132 | |
| 133 private: | |
| 134 EventCallback event_callback_; | |
| 135 }; | |
| 136 | |
| 137 // StubEventRouter factory function | |
| 138 std::unique_ptr<KeyedService> StubEventRouterFactoryFunction( | |
| 139 content::BrowserContext* context) { | |
| 140 return base::WrapUnique(new StubEventRouter(context)); | |
| 141 } | |
| 142 | |
| 143 } // namespace | |
| 144 | |
| 145 class AudioModemApiUnittest : public ExtensionApiUnittest { | |
| 146 public: | |
| 147 AudioModemApiUnittest() {} | |
| 148 ~AudioModemApiUnittest() override {} | |
| 149 | |
| 150 protected: | |
| 151 template <typename Function> | |
| 152 const std::string RunFunction(std::unique_ptr<ListValue> args, | |
| 153 const Extension* extension) { | |
| 154 scoped_refptr<UIThreadExtensionFunction> function(new Function); | |
| 155 function->set_extension(extension); | |
| 156 function->set_browser_context(profile()); | |
| 157 function->set_has_callback(true); | |
| 158 ext_test_utils::RunFunction(function.get(), std::move(args), browser(), | |
| 159 ext_test_utils::NONE); | |
| 160 | |
| 161 std::string result_status; | |
| 162 CHECK(function->GetResultList()->GetString(0, &result_status)); | |
| 163 return result_status; | |
| 164 } | |
| 165 | |
| 166 template <typename Function> | |
| 167 const std::string RunFunction(std::unique_ptr<ListValue> args) { | |
| 168 return RunFunction<Function>(std::move(args), GetExtension(std::string())); | |
| 169 } | |
| 170 | |
| 171 StubModem* GetModem() const { | |
| 172 return g_modems[profile()]; | |
| 173 } | |
| 174 | |
| 175 const Extension* GetExtension(const std::string& name) { | |
| 176 if (!extensions_by_name_[name].get()) { | |
| 177 std::unique_ptr<DictionaryValue> extension_definition( | |
| 178 new DictionaryValue); | |
| 179 extension_definition->SetString("name", name); | |
| 180 extension_definition->SetString("version", "1.0"); | |
| 181 extensions_by_name_[name] = api_test_utils::CreateExtension( | |
| 182 Manifest::INTERNAL, extension_definition.get(), name); | |
| 183 DVLOG(2) << "Created extension " << extensions_by_name_[name]->id(); | |
| 184 } | |
| 185 return extensions_by_name_[name].get(); | |
| 186 } | |
| 187 | |
| 188 const std::vector<std::unique_ptr<const Event>>& GetEventsForExtension( | |
| 189 const std::string& name) { | |
| 190 const Extension* extension = extensions_by_name_[name].get(); | |
| 191 DCHECK(extension); | |
| 192 return events_by_extension_id_[extension->id()]; | |
| 193 } | |
| 194 | |
| 195 const std::vector<std::unique_ptr<const Event>>& GetEvents() { | |
| 196 return GetEventsForExtension(std::string()); | |
| 197 } | |
| 198 | |
| 199 private: | |
| 200 void SetUp() override { | |
| 201 ExtensionApiUnittest::SetUp(); | |
| 202 AudioModemAPI::GetFactoryInstance()->SetTestingFactory( | |
| 203 profile(), &ApiFactoryFunction); | |
| 204 | |
| 205 StubEventRouter* stub_event_router = static_cast<StubEventRouter*>( | |
| 206 extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse( | |
| 207 profile(), &StubEventRouterFactoryFunction)); | |
| 208 stub_event_router->SetEventCallBack(base::Bind( | |
| 209 &AudioModemApiUnittest::CaptureEvent, base::Unretained(this))); | |
| 210 } | |
| 211 | |
| 212 void CaptureEvent(const std::string& extension_id, | |
| 213 std::unique_ptr<Event> event) { | |
| 214 events_by_extension_id_[extension_id].push_back(std::move(event)); | |
| 215 } | |
| 216 | |
| 217 std::map<std::string, scoped_refptr<Extension>> extensions_by_name_; | |
| 218 | |
| 219 std::map<std::string, std::vector<std::unique_ptr<const Event>>> | |
| 220 events_by_extension_id_; | |
| 221 }; | |
| 222 | |
| 223 TEST_F(AudioModemApiUnittest, TransmitBasic) { | |
| 224 // Start transmitting inaudibly. | |
| 225 EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>( | |
| 226 CreateList(CreateParams("inaudible"), CreateToken("1234")))); | |
| 227 EXPECT_TRUE(GetModem()->IsPlaying(INAUDIBLE)); | |
| 228 | |
| 229 // Can't cancel audible transmit - we haven't started it yet. | |
| 230 EXPECT_EQ("invalidRequest", | |
| 231 RunFunction<AudioModemStopTransmitFunction>( | |
| 232 CreateList(base::MakeUnique<StringValue>("audible")))); | |
| 233 | |
| 234 // Start transmitting audibly. | |
| 235 EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>( | |
| 236 CreateList(CreateParams("audible"), CreateToken("ABCD")))); | |
| 237 EXPECT_TRUE(GetModem()->IsPlaying(AUDIBLE)); | |
| 238 | |
| 239 // Stop audible transmit. We're still transmitting inaudibly. | |
| 240 EXPECT_EQ("success", RunFunction<AudioModemStopTransmitFunction>(CreateList( | |
| 241 base::MakeUnique<StringValue>("audible")))); | |
| 242 EXPECT_FALSE(GetModem()->IsPlaying(AUDIBLE)); | |
| 243 EXPECT_TRUE(GetModem()->IsPlaying(INAUDIBLE)); | |
| 244 | |
| 245 // Stop inaudible transmit. | |
| 246 EXPECT_EQ("success", RunFunction<AudioModemStopTransmitFunction>(CreateList( | |
| 247 base::MakeUnique<StringValue>("inaudible")))); | |
| 248 EXPECT_FALSE(GetModem()->IsPlaying(INAUDIBLE)); | |
| 249 } | |
| 250 | |
| 251 TEST_F(AudioModemApiUnittest, ReceiveBasic) { | |
| 252 // Start listening for audible tokens. | |
| 253 EXPECT_EQ("success", RunFunction<AudioModemReceiveFunction>( | |
| 254 CreateList(CreateParams("audible")))); | |
| 255 EXPECT_TRUE(GetModem()->IsRecording(AUDIBLE)); | |
| 256 | |
| 257 // Can't cancel inaudible receive - we haven't started it yet. | |
| 258 EXPECT_EQ("invalidRequest", | |
| 259 RunFunction<AudioModemStopReceiveFunction>( | |
| 260 CreateList(base::MakeUnique<StringValue>("inaudible")))); | |
| 261 | |
| 262 // Send some audible tokens. | |
| 263 std::vector<AudioToken> tokens; | |
| 264 tokens.push_back(AudioToken("1234", true)); | |
| 265 tokens.push_back(AudioToken("ABCD", true)); | |
| 266 tokens.push_back(AudioToken("abcd", false)); | |
| 267 GetModem()->DeliverTokens(tokens); | |
| 268 | |
| 269 // Check the tokens received. | |
| 270 EXPECT_EQ(1u, GetEvents().size()); | |
| 271 std::unique_ptr<ListValue> expected_tokens(new ListValue); | |
| 272 expected_tokens->Append(CreateReceivedToken("1234", "audible")); | |
| 273 expected_tokens->Append(CreateReceivedToken("ABCD", "audible")); | |
| 274 ListValue* received_tokens; | |
| 275 GetEvents()[0]->event_args->GetList(0, &received_tokens); | |
| 276 EXPECT_TRUE(received_tokens->Equals(expected_tokens.get())); | |
| 277 | |
| 278 // Start listening for inaudible tokens. | |
| 279 EXPECT_EQ("success", RunFunction<AudioModemReceiveFunction>( | |
| 280 CreateList(CreateParams("inaudible")))); | |
| 281 EXPECT_TRUE(GetModem()->IsRecording(INAUDIBLE)); | |
| 282 | |
| 283 // Send some more tokens. | |
| 284 tokens.push_back(AudioToken("5678", false)); | |
| 285 GetModem()->DeliverTokens(tokens); | |
| 286 | |
| 287 // Check the tokens received. | |
| 288 EXPECT_EQ(2u, GetEvents().size()); | |
| 289 expected_tokens->Append(CreateReceivedToken("abcd", "inaudible")); | |
| 290 expected_tokens->Append(CreateReceivedToken("5678", "inaudible")); | |
| 291 GetEvents()[1]->event_args->GetList(0, &received_tokens); | |
| 292 EXPECT_TRUE(received_tokens->Equals(expected_tokens.get())); | |
| 293 | |
| 294 // Stop audible receive. We're still receiving inaudible. | |
| 295 EXPECT_EQ("success", RunFunction<AudioModemStopReceiveFunction>(CreateList( | |
| 296 base::MakeUnique<StringValue>("audible")))); | |
| 297 EXPECT_FALSE(GetModem()->IsRecording(AUDIBLE)); | |
| 298 EXPECT_TRUE(GetModem()->IsRecording(INAUDIBLE)); | |
| 299 | |
| 300 // Stop inaudible receive. | |
| 301 EXPECT_EQ("success", RunFunction<AudioModemStopReceiveFunction>(CreateList( | |
| 302 base::MakeUnique<StringValue>("inaudible")))); | |
| 303 EXPECT_FALSE(GetModem()->IsRecording(INAUDIBLE)); | |
| 304 } | |
| 305 | |
| 306 TEST_F(AudioModemApiUnittest, TransmitMultiple) { | |
| 307 // Start transmit. | |
| 308 EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>( | |
| 309 CreateList(CreateParams("audible"), CreateToken("1234")), | |
| 310 GetExtension("ext1"))); | |
| 311 EXPECT_TRUE(GetModem()->IsPlaying(AUDIBLE)); | |
| 312 | |
| 313 // Another extension can't interfere with the first one. | |
| 314 EXPECT_EQ("inUse", RunFunction<AudioModemTransmitFunction>( | |
| 315 CreateList(CreateParams("audible"), CreateToken("ABCD")), | |
| 316 GetExtension("ext2"))); | |
| 317 EXPECT_EQ("invalidRequest", | |
| 318 RunFunction<AudioModemStopTransmitFunction>( | |
| 319 CreateList(base::MakeUnique<StringValue>("audible")), | |
| 320 GetExtension("ext2"))); | |
| 321 EXPECT_TRUE(GetModem()->IsPlaying(AUDIBLE)); | |
| 322 | |
| 323 // The other extension can use the other audio band, however. | |
| 324 EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>( | |
| 325 CreateList(CreateParams("inaudible"), CreateToken("ABCD")), | |
| 326 GetExtension("ext2"))); | |
| 327 EXPECT_TRUE(GetModem()->IsPlaying(INAUDIBLE)); | |
| 328 | |
| 329 // The first extension can change its token. | |
| 330 // But the other band is still in use. | |
| 331 EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>( | |
| 332 CreateList(CreateParams("audible"), CreateToken("abcd")), | |
| 333 GetExtension("ext1"))); | |
| 334 EXPECT_EQ("inUse", RunFunction<AudioModemTransmitFunction>( | |
| 335 CreateList(CreateParams("inaudible"), CreateToken("1234")), | |
| 336 GetExtension("ext1"))); | |
| 337 | |
| 338 // Stop transmission. | |
| 339 EXPECT_EQ("success", RunFunction<AudioModemStopTransmitFunction>( | |
| 340 CreateList(base::MakeUnique<StringValue>("audible")), | |
| 341 GetExtension("ext1"))); | |
| 342 EXPECT_FALSE(GetModem()->IsPlaying(AUDIBLE)); | |
| 343 EXPECT_EQ("success", | |
| 344 RunFunction<AudioModemStopTransmitFunction>( | |
| 345 CreateList(base::MakeUnique<StringValue>("inaudible")), | |
| 346 GetExtension("ext2"))); | |
| 347 EXPECT_FALSE(GetModem()->IsPlaying(INAUDIBLE)); | |
| 348 } | |
| 349 | |
| 350 TEST_F(AudioModemApiUnittest, ReceiveMultiple) { | |
| 351 // Start receive. Multiple extensions can receive on the same band. | |
| 352 EXPECT_EQ("success", RunFunction<AudioModemReceiveFunction>( | |
| 353 CreateList(CreateParams("inaudible")), GetExtension("ext1"))); | |
| 354 EXPECT_TRUE(GetModem()->IsRecording(INAUDIBLE)); | |
| 355 EXPECT_EQ("success", RunFunction<AudioModemReceiveFunction>( | |
| 356 CreateList(CreateParams("inaudible")), GetExtension("ext2"))); | |
| 357 | |
| 358 // Receive a token. | |
| 359 GetModem()->DeliverTokens(std::vector<AudioToken>( | |
| 360 1, AudioToken("abcd", false))); | |
| 361 EXPECT_EQ(1u, GetEventsForExtension("ext1").size()); | |
| 362 EXPECT_EQ(1u, GetEventsForExtension("ext2").size()); | |
| 363 | |
| 364 // Check the token received. | |
| 365 std::unique_ptr<DictionaryValue> expected_token( | |
| 366 CreateReceivedToken("abcd", "inaudible")); | |
| 367 EXPECT_TRUE(ReceivedSingleToken(GetEventsForExtension("ext1")[0].get(), | |
| 368 expected_token.get())); | |
| 369 EXPECT_TRUE(ReceivedSingleToken(GetEventsForExtension("ext2")[0].get(), | |
| 370 expected_token.get())); | |
| 371 | |
| 372 // If one extension stops, the modem is still receiving for the other. | |
| 373 EXPECT_EQ("success", | |
| 374 RunFunction<AudioModemStopReceiveFunction>( | |
| 375 CreateList(base::MakeUnique<StringValue>("inaudible")), | |
| 376 GetExtension("ext1"))); | |
| 377 EXPECT_TRUE(GetModem()->IsRecording(INAUDIBLE)); | |
| 378 | |
| 379 // Receive another token. Should only go to ext2. | |
| 380 GetModem()->DeliverTokens(std::vector<AudioToken>( | |
| 381 1, AudioToken("1234", false))); | |
| 382 EXPECT_EQ(1u, GetEventsForExtension("ext1").size()); | |
| 383 EXPECT_EQ(2u, GetEventsForExtension("ext2").size()); | |
| 384 expected_token.reset(CreateReceivedToken("1234", "inaudible")); | |
| 385 EXPECT_TRUE(ReceivedSingleToken(GetEventsForExtension("ext2")[1].get(), | |
| 386 expected_token.get())); | |
| 387 | |
| 388 EXPECT_EQ("success", | |
| 389 RunFunction<AudioModemStopReceiveFunction>( | |
| 390 CreateList(base::MakeUnique<StringValue>("inaudible")), | |
| 391 GetExtension("ext2"))); | |
| 392 EXPECT_FALSE(GetModem()->IsRecording(INAUDIBLE)); | |
| 393 } | |
| 394 | |
| 395 } // namespace extensions | |
| OLD | NEW |