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 |