blob: a12afd6a92638b50addf902a9e5e540101603cfa [file] [log] [blame]
Tomasz Wasilczyk31e86322017-12-05 09:36:11 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "BcRadio.vts"
18
19#include <VtsHalHidlTargetTestBase.h>
20#include <android-base/logging.h>
21#include <android/hardware/broadcastradio/2.0/IBroadcastRadio.h>
22#include <android/hardware/broadcastradio/2.0/ITunerCallback.h>
23#include <android/hardware/broadcastradio/2.0/ITunerSession.h>
24#include <android/hardware/broadcastradio/2.0/types.h>
25#include <broadcastradio-utils-2x/Utils.h>
26#include <broadcastradio-vts-utils/call-barrier.h>
27#include <broadcastradio-vts-utils/mock-timeout.h>
28#include <broadcastradio-vts-utils/pointer-utils.h>
29#include <gmock/gmock.h>
30
31#include <chrono>
32
33namespace android {
34namespace hardware {
35namespace broadcastradio {
36namespace V2_0 {
37namespace vts {
38
39using namespace std::chrono_literals;
40
41using std::vector;
42using testing::_;
43using testing::AnyNumber;
44using testing::ByMove;
45using testing::DoAll;
46using testing::Invoke;
47using testing::SaveArg;
48
49using broadcastradio::vts::CallBarrier;
50using broadcastradio::vts::clearAndWait;
51using utils::make_identifier;
52using utils::make_selector_amfm;
53
54namespace timeout {
55
56static constexpr auto tune = 30s;
57
58} // namespace timeout
59
60struct TunerCallbackMock : public ITunerCallback {
61 TunerCallbackMock() {
62 // we expect the antenna is connected through the whole test
63 EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
64 }
65
66 MOCK_METHOD2(onTuneFailed, Return<void>(Result, const ProgramSelector&));
67 MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChanged, Return<void>(const ProgramInfo&));
68 MOCK_METHOD1(onAntennaStateChange, Return<void>(bool connected));
69 MOCK_METHOD1(onParametersUpdated, Return<void>(const hidl_vec<VendorKeyValue>& parameters));
70};
71
72class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
73 protected:
74 virtual void SetUp() override;
75 virtual void TearDown() override;
76
77 bool openSession();
78
79 sp<IBroadcastRadio> mModule;
80 Properties mProperties;
81 sp<ITunerSession> mSession;
82 sp<TunerCallbackMock> mCallback = new TunerCallbackMock();
83};
84
85void BroadcastRadioHalTest::SetUp() {
86 EXPECT_EQ(nullptr, mModule.get()) << "Module is already open";
87
88 // lookup HIDL service (radio module)
89 mModule = getService<IBroadcastRadio>();
90 ASSERT_NE(nullptr, mModule.get()) << "Couldn't find broadcast radio HAL implementation";
91
92 // get module properties
93 auto propResult = mModule->getProperties([&](const Properties& p) { mProperties = p; });
94 ASSERT_TRUE(propResult.isOk());
95
96 EXPECT_FALSE(mProperties.maker.empty());
97 EXPECT_FALSE(mProperties.product.empty());
98 EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
99}
100
101void BroadcastRadioHalTest::TearDown() {
102 mSession.clear();
103 mModule.clear();
104 clearAndWait(mCallback, 1s);
105}
106
107bool BroadcastRadioHalTest::openSession() {
108 EXPECT_EQ(nullptr, mSession.get()) << "Session is already open";
109
110 Result halResult = Result::UNKNOWN_ERROR;
111 auto openCb = [&](Result result, const sp<ITunerSession>& session) {
112 halResult = result;
113 if (result != Result::OK) return;
114 mSession = session;
115 };
116 auto hidlResult = mModule->openSession(mCallback, openCb);
117
118 EXPECT_TRUE(hidlResult.isOk());
119 EXPECT_EQ(Result::OK, halResult);
120 EXPECT_NE(nullptr, mSession.get());
121
122 return nullptr != mSession.get();
123}
124
125/**
126 * Test session opening.
127 *
128 * Verifies that:
129 * - the method succeeds on a first and subsequent calls;
130 * - the method succeeds when called for the second time without
131 * closing previous session.
132 */
133TEST_F(BroadcastRadioHalTest, OpenSession) {
134 // simply open session for the first time
135 ASSERT_TRUE(openSession());
136
137 // drop (without explicit close) and re-open the session
138 mSession.clear();
139 ASSERT_TRUE(openSession());
140
141 // open the second session (the first one should be forcibly closed)
142 auto secondSession = mSession;
143 mSession.clear();
144 ASSERT_TRUE(openSession());
145}
146
147/**
148 * Test tuning with FM selector.
149 *
150 * Verifies that:
151 * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
152 * - if it is supported, the method succeeds;
153 * - after a successful tune call, onCurrentProgramInfoChanged callback is
154 * invoked carrying a proper selector;
155 * - program changes exactly to what was requested.
156 */
157TEST_F(BroadcastRadioHalTest, FmTune) {
158 ASSERT_TRUE(openSession());
159
160 uint64_t freq = 100100; // 100.1 FM
161 auto sel = make_selector_amfm(freq);
162
163 // try tuning
164 ProgramInfo infoCb = {};
165 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _)
166 .Times(AnyNumber())
167 .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
168 auto result = mSession->tune(sel);
169
170 // expect a failure if it's not supported
171 if (!utils::isSupported(mProperties, sel)) {
172 EXPECT_EQ(Result::NOT_SUPPORTED, result);
173 return;
174 }
175
176 // expect a callback if it succeeds
177 EXPECT_EQ(Result::OK, result);
178 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
179
180 // it should tune exactly to what was requested
181 auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY);
182 EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
183}
184
185/**
186 * Test tuning with invalid selectors.
187 *
188 * Verifies that:
189 * - if the selector is not supported, it's ignored;
190 * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
191 */
192TEST_F(BroadcastRadioHalTest, TuneFailsWithInvalid) {
193 ASSERT_TRUE(openSession());
194
195 vector<ProgramIdentifier> invalid = {
196 make_identifier(IdentifierType::AMFM_FREQUENCY, 0),
197 make_identifier(IdentifierType::RDS_PI, 0x10000),
198 make_identifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
199 make_identifier(IdentifierType::DAB_SID_EXT, 0),
200 make_identifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
201 make_identifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
202 };
203
204 for (auto&& id : invalid) {
205 ProgramSelector sel{id, {}};
206
207 auto result = mSession->tune(sel);
208
209 if (utils::isSupported(mProperties, sel)) {
210 EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
211 } else {
212 EXPECT_EQ(Result::NOT_SUPPORTED, result);
213 }
214 }
215}
216
217/**
218 * Test tuning with empty program selector.
219 *
220 * Verifies that:
221 * - tune fails with NOT_SUPPORTED when program selector is not initialized.
222 */
223TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) {
224 ASSERT_TRUE(openSession());
225
226 // Program type is 1-based, so 0 will always be invalid.
227 ProgramSelector sel = {};
228 auto result = mSession->tune(sel);
229 ASSERT_EQ(Result::NOT_SUPPORTED, result);
230}
231
232/**
233 * Test scanning to next/prev station.
234 *
235 * Verifies that:
236 * - the method succeeds;
237 * - the program info is changed within timeout::tune;
238 * - works both directions and with or without skipping sub-channel.
239 */
240TEST_F(BroadcastRadioHalTest, Scan) {
241 ASSERT_TRUE(openSession());
242
243 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
244 auto result = mSession->scan(true /* up */, true /* skip subchannel */);
245 EXPECT_EQ(Result::OK, result);
246 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
247
248 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
249 result = mSession->scan(false /* down */, false /* don't skip subchannel */);
250 EXPECT_EQ(Result::OK, result);
251 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
252}
253
254/**
255 * Test step operation.
256 *
257 * Verifies that:
258 * - the method succeeds or returns NOT_SUPPORTED;
259 * - the program info is changed within timeout::tune if the method succeeded;
260 * - works both directions.
261 */
262TEST_F(BroadcastRadioHalTest, Step) {
263 ASSERT_TRUE(openSession());
264
265 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _).Times(AnyNumber());
266 auto result = mSession->step(true /* up */);
267 if (result == Result::NOT_SUPPORTED) return;
268 EXPECT_EQ(Result::OK, result);
269 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
270
271 EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _);
272 result = mSession->step(false /* down */);
273 EXPECT_EQ(Result::OK, result);
274 EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune);
275}
276
277/**
278 * Test tune cancellation.
279 *
280 * Verifies that:
281 * - the method does not crash after being invoked multiple times.
282 */
283TEST_F(BroadcastRadioHalTest, Cancel) {
284 ASSERT_TRUE(openSession());
285
286 for (int i = 0; i < 10; i++) {
287 auto scanResult = mSession->scan(true /* up */, true /* skip subchannel */);
288 ASSERT_EQ(Result::OK, scanResult);
289
290 auto cancelResult = mSession->cancel();
291 ASSERT_TRUE(cancelResult.isOk());
292 }
293}
294
295/**
296 * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
297 *
298 * Verifies that:
299 * - callback is called for empty parameters set.
300 */
301TEST_F(BroadcastRadioHalTest, NoParameters) {
302 ASSERT_TRUE(openSession());
303
304 hidl_vec<VendorKeyValue> halResults = {};
305 bool wasCalled = false;
306 auto cb = [&](hidl_vec<VendorKeyValue> results) {
307 wasCalled = true;
308 halResults = results;
309 };
310
311 auto hidlResult = mSession->setParameters({}, cb);
312 ASSERT_TRUE(hidlResult.isOk());
313 ASSERT_TRUE(wasCalled);
314 ASSERT_EQ(0u, halResults.size());
315
316 wasCalled = false;
317 hidlResult = mSession->getParameters({}, cb);
318 ASSERT_TRUE(hidlResult.isOk());
319 ASSERT_TRUE(wasCalled);
320 ASSERT_EQ(0u, halResults.size());
321}
322
323/**
324 * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
325 *
326 * Verifies that:
327 * - unknown parameters are ignored;
328 * - callback is called also for empty results set.
329 */
330TEST_F(BroadcastRadioHalTest, UnknownParameters) {
331 ASSERT_TRUE(openSession());
332
333 hidl_vec<VendorKeyValue> halResults = {};
334 bool wasCalled = false;
335 auto cb = [&](hidl_vec<VendorKeyValue> results) {
336 wasCalled = true;
337 halResults = results;
338 };
339
340 auto hidlResult = mSession->setParameters({{"com.google.unknown", "dummy"}}, cb);
341 ASSERT_TRUE(hidlResult.isOk());
342 ASSERT_TRUE(wasCalled);
343 ASSERT_EQ(0u, halResults.size());
344
345 wasCalled = false;
346 hidlResult = mSession->getParameters({{"com.google.unknown*", "dummy"}}, cb);
347 ASSERT_TRUE(hidlResult.isOk());
348 ASSERT_TRUE(wasCalled);
349 ASSERT_EQ(0u, halResults.size());
350}
351
352/**
353 * Test session closing.
354 *
355 * Verifies that:
356 * - the method does not crash after being invoked multiple times.
357 */
358TEST_F(BroadcastRadioHalTest, Close) {
359 ASSERT_TRUE(openSession());
360
361 for (int i = 0; i < 10; i++) {
362 auto cancelResult = mSession->close();
363 ASSERT_TRUE(cancelResult.isOk());
364 }
365}
366
367/**
368 * Test geting image of invalid ID.
369 *
370 * Verifies that:
371 * - getImage call handles argument 0 gracefully.
372 */
373TEST_F(BroadcastRadioHalTest, GetNoImage) {
374 size_t len = 0;
375 auto result = mModule->getImage(0, [&](hidl_vec<uint8_t> rawImage) { len = rawImage.size(); });
376
377 ASSERT_TRUE(result.isOk());
378 ASSERT_EQ(0u, len);
379}
380
381} // namespace vts
382} // namespace V2_0
383} // namespace broadcastradio
384} // namespace hardware
385} // namespace android
386
387int main(int argc, char** argv) {
388 ::testing::InitGoogleTest(&argc, argv);
389 int status = RUN_ALL_TESTS();
390 ALOGI("Test result = %d", status);
391 return status;
392}