2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.fsinternetradio.test;
15 import static org.hamcrest.CoreMatchers.is;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.*;
18 import static org.mockito.ArgumentMatchers.isA;
19 import static org.mockito.Mockito.*;
20 import static org.openhab.binding.fsinternetradio.internal.FSInternetRadioBindingConstants.*;
22 import java.io.IOException;
23 import java.math.BigDecimal;
26 import org.apache.commons.lang.StringUtils;
27 import org.eclipse.jdt.annotation.NonNull;
28 import org.eclipse.jetty.client.HttpClient;
29 import org.eclipse.jetty.servlet.ServletHolder;
30 import org.junit.jupiter.api.AfterAll;
31 import org.junit.jupiter.api.BeforeAll;
32 import org.junit.jupiter.api.BeforeEach;
33 import org.junit.jupiter.api.Test;
34 import org.mockito.ArgumentCaptor;
35 import org.openhab.binding.fsinternetradio.internal.FSInternetRadioBindingConstants;
36 import org.openhab.binding.fsinternetradio.internal.handler.FSInternetRadioHandler;
37 import org.openhab.binding.fsinternetradio.internal.handler.HandlerUtils;
38 import org.openhab.binding.fsinternetradio.internal.radio.FrontierSiliconRadio;
39 import org.openhab.core.config.core.Configuration;
40 import org.openhab.core.items.Item;
41 import org.openhab.core.library.items.DimmerItem;
42 import org.openhab.core.library.items.NumberItem;
43 import org.openhab.core.library.items.StringItem;
44 import org.openhab.core.library.items.SwitchItem;
45 import org.openhab.core.library.types.DecimalType;
46 import org.openhab.core.library.types.IncreaseDecreaseType;
47 import org.openhab.core.library.types.OnOffType;
48 import org.openhab.core.library.types.PercentType;
49 import org.openhab.core.library.types.UpDownType;
50 import org.openhab.core.test.TestPortUtil;
51 import org.openhab.core.test.TestServer;
52 import org.openhab.core.test.java.JavaTest;
53 import org.openhab.core.thing.Channel;
54 import org.openhab.core.thing.ChannelUID;
55 import org.openhab.core.thing.Thing;
56 import org.openhab.core.thing.ThingStatus;
57 import org.openhab.core.thing.ThingStatusDetail;
58 import org.openhab.core.thing.ThingStatusInfo;
59 import org.openhab.core.thing.ThingTypeUID;
60 import org.openhab.core.thing.ThingUID;
61 import org.openhab.core.thing.binding.ThingHandlerCallback;
62 import org.openhab.core.thing.binding.builder.ChannelBuilder;
63 import org.openhab.core.thing.binding.builder.ThingBuilder;
64 import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
65 import org.openhab.core.types.UnDefType;
68 * OSGi tests for the {@link FSInternetRadioHandler}.
70 * @author Mihaela Memova - Initial contribution
71 * @author Markus Rathgeb - Migrated from Groovy to pure Java test, made more robust
72 * @author Velin Yordanov - Migrated to mockito
75 public class FSInternetRadioHandlerJavaTest extends JavaTest {
76 private static final String DEFAULT_TEST_THING_NAME = "testRadioThing";
77 private static final String DEFAULT_TEST_ITEM_NAME = "testItem";
78 private final String VOLUME = "volume";
80 // The request send for preset is "SET/netRemote.nav.action.selectPreset";
81 private static final String PRESET = "Preset";
82 private static final int TIMEOUT = 10 * 1000;
83 private static final ThingTypeUID DEFAULT_THING_TYPE_UID = FSInternetRadioBindingConstants.THING_TYPE_RADIO;
84 private static final ThingUID DEFAULT_THING_UID = new ThingUID(DEFAULT_THING_TYPE_UID, DEFAULT_TEST_THING_NAME);
85 private static final RadioServiceDummy radioServiceDummy = new RadioServiceDummy();
88 * In order to test a specific channel, it is necessary to create a Thing with two channels - CHANNEL_POWER
89 * and the tested channel. So before each test, the power channel is created and added
90 * to an ArrayList of channels. Then in the tests an additional channel is created and added to the ArrayList
93 private Channel powerChannel;
95 private ThingHandlerCallback callback;
97 private static TestServer server;
100 * A HashMap which saves all the 'channel-acceppted_item_type' pairs.
101 * It is set before all the tests.
103 private static Map<String, String> acceptedItemTypes;
106 * ArrayList of channels which is used to initialize a radioThing in the test cases.
108 private final List<Channel> channels = new ArrayList<>();
110 private FSInternetRadioHandler radioHandler;
111 private Thing radioThing;
113 private static HttpClient httpClient;
115 // default configuration properties
116 private static final String DEFAULT_CONFIG_PROPERTY_IP = "127.0.0.1";
117 private static final String DEFAULT_CONFIG_PROPERTY_PIN = "1234";
118 private static final int DEFAULT_CONFIG_PROPERTY_PORT = TestPortUtil.findFreePort();
120 /** The default refresh interval is 60 seconds. For the purposes of the tests it is set to 1 second */
121 private static final String DEFAULT_CONFIG_PROPERTY_REFRESH = "1";
122 private static final Configuration DEFAULT_COMPLETE_CONFIGURATION = createDefaultConfiguration();
125 public static void setUpClass() throws Exception {
126 ServletHolder holder = new ServletHolder(radioServiceDummy);
127 server = new TestServer(DEFAULT_CONFIG_PROPERTY_IP, DEFAULT_CONFIG_PROPERTY_PORT, TIMEOUT, holder);
129 server.startServer();
130 httpClient = new HttpClient();
135 public void setUp() {
136 createThePowerChannel();
140 public static void tearDownClass() throws Exception {
145 private static @NonNull Channel getChannel(final @NonNull Thing thing, final @NonNull String channelId) {
146 final Channel channel = thing.getChannel(channelId);
147 assertNotNull(channel);
148 Objects.requireNonNull(channel);
152 private static @NonNull ChannelUID getChannelUID(final @NonNull Thing thing, final @NonNull String channelId) {
153 final ChannelUID channelUID = getChannel(thing, channelId).getUID();
154 assertNotNull(channelUID);
159 * Verify OFFLINE Thing status when the IP is NULL.
162 public void offlineIfNullIp() {
163 Configuration config = createConfiguration(null, DEFAULT_CONFIG_PROPERTY_PIN,
164 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
165 Thing radioThingWithNullIP = initializeRadioThing(config);
166 testRadioThingConsideringConfiguration(radioThingWithNullIP);
170 * Verify OFFLINE Thing status when the PIN is empty String.
173 public void offlineIfEmptyPIN() {
174 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, "",
175 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
176 Thing radioThingWithEmptyPIN = initializeRadioThing(config);
177 testRadioThingConsideringConfiguration(radioThingWithEmptyPIN);
181 * Verify OFFLINE Thing status when the PORT is zero.
184 public void offlineIfZeroPort() {
185 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, DEFAULT_CONFIG_PROPERTY_PIN, "0",
186 DEFAULT_CONFIG_PROPERTY_REFRESH);
187 Thing radioThingWithZeroPort = initializeRadioThing(config);
188 testRadioThingConsideringConfiguration(radioThingWithZeroPort);
192 * Verify OFFLINE Thing status when the PIN is wrong.
195 public void offlineIfWrongPIN() {
196 final String wrongPin = "5678";
197 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, wrongPin,
198 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
199 initializeRadioThing(config);
200 waitForAssert(() -> {
201 String exceptionMessage = "Radio does not allow connection, maybe wrong pin?";
202 verifyCommunicationError(exceptionMessage);
207 * Verify OFFLINE Thing status when the HTTP response cannot be parsed correctly.
210 public void offlineIfParseError() {
211 // create a thing with two channels - the power channel and any of the others
212 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
213 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
214 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
216 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
217 testRadioThingConsideringConfiguration(radioThing);
219 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
222 * Setting the isInvalidResponseExpected variable to true
223 * in order to get the incorrect XML response from the servlet
225 radioServiceDummy.setInvalidResponse(true);
227 // try to handle a command
228 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
230 waitForAssert(() -> {
231 String exceptionMessage = "java.io.IOException: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 2;";
232 verifyCommunicationError(exceptionMessage);
234 radioServiceDummy.setInvalidResponse(false);
238 * Verify the HTTP status is handled correctly when it is not OK_200.
241 public void httpStatusNokHandling() {
242 // create a thing with two channels - the power channel and any of the others
243 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
244 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
245 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
247 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
248 testRadioThingConsideringConfiguration(radioThing);
251 turnTheRadioOn(radioThing);
254 * Setting the needed boolean variable to false, so we can be sure
255 * that the XML response won't have a OK_200 status
257 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
258 Item modeTestItem = initializeItem(modeChannelUID, CHANNEL_MODE, acceptedItemType);
260 // try to handle a command
261 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
263 waitForAssert(() -> {
264 assertSame(UnDefType.NULL, modeTestItem.getState());
269 * Verify ONLINE status of a Thing with complete configuration.
272 public void verifyOnlineStatus() {
273 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
274 testRadioThingConsideringConfiguration(radioThing);
278 * Verify the power channel is updated.
281 public void powerChannelUpdated() {
282 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
283 testRadioThingConsideringConfiguration(radioThing);
285 ChannelUID powerChannelUID = powerChannel.getUID();
286 initializeItem(powerChannelUID, DEFAULT_TEST_ITEM_NAME,
287 acceptedItemTypes.get(FSInternetRadioBindingConstants.CHANNEL_POWER));
289 radioHandler.handleCommand(powerChannelUID, OnOffType.ON);
290 waitForAssert(() -> {
291 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER),
292 "We should be able to turn on the radio");
293 radioServiceDummy.clearRequestParameters();
296 radioHandler.handleCommand(powerChannelUID, OnOffType.OFF);
297 waitForAssert(() -> {
298 assertTrue(radioServiceDummy.containsRequestParameter(0, CHANNEL_POWER),
299 "We should be able to turn off the radio");
300 radioServiceDummy.clearRequestParameters();
304 * Setting the needed boolean variable to true, so we can be sure
305 * that an invalid value will be returned in the XML response
307 radioHandler.handleCommand(powerChannelUID, OnOffType.ON);
308 waitForAssert(() -> {
309 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER),
310 "We should be able to turn on the radio");
311 radioServiceDummy.clearRequestParameters();
316 * Verify the mute channel is updated.
319 public void muteChhannelUpdated() {
320 String muteChannelID = FSInternetRadioBindingConstants.CHANNEL_MUTE;
321 String acceptedItemType = acceptedItemTypes.get(muteChannelID);
322 createChannel(DEFAULT_THING_UID, muteChannelID, acceptedItemType);
324 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
325 testRadioThingConsideringConfiguration(radioThing);
327 turnTheRadioOn(radioThing);
329 ChannelUID muteChannelUID = getChannelUID(radioThing, FSInternetRadioBindingConstants.CHANNEL_MUTE);
330 initializeItem(muteChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
332 radioHandler.handleCommand(muteChannelUID, OnOffType.ON);
333 waitForAssert(() -> {
334 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_MUTE),
335 "We should be able to mute the radio");
336 radioServiceDummy.clearRequestParameters();
339 radioHandler.handleCommand(muteChannelUID, OnOffType.OFF);
340 waitForAssert(() -> {
341 assertTrue(radioServiceDummy.containsRequestParameter(0, CHANNEL_MUTE),
342 "We should be able to unmute the radio");
343 radioServiceDummy.clearRequestParameters();
347 * Setting the needed boolean variable to true, so we can be sure
348 * that an invalid value will be returned in the XML response
353 * Verify the mode channel is updated.
356 public void modeChannelUdpated() {
357 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
358 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
359 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
361 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
362 testRadioThingConsideringConfiguration(radioThing);
364 turnTheRadioOn(radioThing);
366 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
367 initializeItem(modeChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
369 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
370 waitForAssert(() -> {
371 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_MODE),
372 "We should be able to update the mode channel correctly");
373 radioServiceDummy.clearRequestParameters();
377 * Setting the needed boolean variable to true, so we can be sure
378 * that an invalid value will be returned in the XML response
380 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("3"));
381 waitForAssert(() -> {
382 assertTrue(radioServiceDummy.containsRequestParameter(3, CHANNEL_MODE),
383 "We should be able to update the mode channel correctly");
384 radioServiceDummy.clearRequestParameters();
389 * Verify the volume is updated through the CHANNEL_VOLUME_ABSOLUTE using INCREASE and DECREASE commands.
392 public void volumechannelUpdatedAbsIncDec() {
393 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
394 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
395 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
397 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
398 testRadioThingConsideringConfiguration(radioThing);
400 turnTheRadioOn(radioThing);
402 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
403 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
404 absoluteAcceptedItemType);
406 testChannelWithINCREASEAndDECREASECommands(absoluteVolumeChannelUID, volumeTestItem);
410 * Verify the volume is updated through the CHANNEL_VOLUME_ABSOLUTE using UP and DOWN commands.
413 public void volumeChannelUpdatedAbsUpDown() {
414 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
415 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
416 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
418 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
419 testRadioThingConsideringConfiguration(radioThing);
421 turnTheRadioOn(radioThing);
423 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
424 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
425 absoluteAcceptedItemType);
427 testChannelWithUPAndDOWNCommands(absoluteVolumeChannelUID, volumeTestItem);
431 * Verify the invalid values when updating CHANNEL_VOLUME_ABSOLUTE are handled correctly.
434 public void invalidAbsVolumeValues() {
435 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
436 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
437 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
439 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
440 testRadioThingConsideringConfiguration(radioThing);
442 turnTheRadioOn(radioThing);
444 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
445 initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME, absoluteAcceptedItemType);
447 // Trying to set a value that is greater than the maximum volume
448 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("36"));
450 waitForAssert(() -> {
451 assertTrue(radioServiceDummy.containsRequestParameter(32, VOLUME),
452 "The volume should not exceed the maximum value");
453 radioServiceDummy.clearRequestParameters();
456 // Trying to increase the volume more than its maximum value using the INCREASE command
457 radioHandler.handleCommand(absoluteVolumeChannelUID, IncreaseDecreaseType.INCREASE);
459 waitForAssert(() -> {
460 assertTrue(radioServiceDummy.areRequestParametersEmpty(),
461 "The volume should not be increased above the maximum value");
462 radioServiceDummy.clearRequestParameters();
465 // Trying to increase the volume more than its maximum value using the UP command
466 radioHandler.handleCommand(absoluteVolumeChannelUID, UpDownType.UP);
468 waitForAssert(() -> {
469 assertTrue(radioServiceDummy.areRequestParametersEmpty(),
470 "The volume should not be increased above the maximum value");
471 radioServiceDummy.clearRequestParameters();
474 // Trying to set a value that is lower than the minimum volume value
475 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("-10"));
476 waitForAssert(() -> {
477 assertTrue(radioServiceDummy.containsRequestParameter(0, VOLUME),
478 "The volume should not be decreased below 0");
479 radioServiceDummy.clearRequestParameters();
483 * Setting the needed boolean variable to true, so we can be sure
484 * that an invalid value will be returned in the XML response
487 // trying to set the volume
488 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("15"));
489 waitForAssert(() -> {
490 assertTrue(radioServiceDummy.containsRequestParameter(15, VOLUME),
491 "We should be able to set the volume correctly");
492 radioServiceDummy.clearRequestParameters();
497 * Verify the volume is updated through the CHANNEL_VOLUME_PERCENT using INCREASE and DECREASE commands.
500 public void volumeChannelUpdatedPercIncDec() {
502 * The volume is set through the CHANNEL_VOLUME_PERCENT in order to check if
503 * the absolute volume will be updated properly.
505 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
506 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
507 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
509 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
510 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
511 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
513 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
514 testRadioThingConsideringConfiguration(radioThing);
516 turnTheRadioOn(radioThing);
518 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
519 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
520 absoluteAcceptedItemType);
522 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
524 testChannelWithINCREASEAndDECREASECommands(percentVolumeChannelUID, volumeTestItem);
528 * Verify the volume is updated through the CHANNEL_VOLUME_PERCENT using UP and DOWN commands.
531 public void volumeChannelUpdatedPercUpDown() {
533 * The volume is set through the CHANNEL_VOLUME_PERCENT in order to check if
534 * the absolute volume will be updated properly.
536 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
537 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
538 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
540 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
541 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
542 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
544 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
545 testRadioThingConsideringConfiguration(radioThing);
547 turnTheRadioOn(radioThing);
549 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
550 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
551 absoluteAcceptedItemType);
553 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
555 testChannelWithUPAndDOWNCommands(percentVolumeChannelUID, volumeTestItem);
559 * Verify the valid and invalid values when updating CHANNEL_VOLUME_PERCENT are handled correctly.
562 public void validInvalidPercVolume() {
563 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
564 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
565 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
567 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
568 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
569 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
571 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
572 testRadioThingConsideringConfiguration(radioThing);
574 turnTheRadioOn(radioThing);
576 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
577 initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME, absoluteAcceptedItemType);
579 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
582 * Giving the handler a valid percent value. According to the FrontierSiliconRadio's
583 * documentation 100 percents correspond to 32 absolute value
585 radioHandler.handleCommand(percentVolumeChannelUID, PercentType.valueOf("50"));
586 waitForAssert(() -> {
587 assertTrue(radioServiceDummy.containsRequestParameter(16, VOLUME),
588 "We should be able to set the volume correctly using percentages.");
589 radioServiceDummy.clearRequestParameters();
592 radioHandler.handleCommand(percentVolumeChannelUID, PercentType.valueOf("15"));
594 waitForAssert(() -> {
595 assertTrue(radioServiceDummy.containsRequestParameter(4, VOLUME),
596 "We should be able to set the volume correctly using percentages.");
597 radioServiceDummy.clearRequestParameters();
601 private void testChannelWithINCREASEAndDECREASECommands(ChannelUID channelUID, Item item) {
602 synchronized (channelUID) {
603 // First we have to make sure that the item state is 0
604 radioHandler.handleCommand(channelUID, DecimalType.valueOf("0"));
605 waitForAssert(() -> {
606 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER),
607 "We should be able to turn on the radio");
608 radioServiceDummy.clearRequestParameters();
611 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.INCREASE);
613 waitForAssert(() -> {
614 assertTrue(radioServiceDummy.containsRequestParameter(1, VOLUME),
615 "We should be able to increase the volume correctly");
616 radioServiceDummy.clearRequestParameters();
619 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.DECREASE);
620 waitForAssert(() -> {
621 assertTrue(radioServiceDummy.containsRequestParameter(0, VOLUME),
622 "We should be able to increase the volume correctly");
623 radioServiceDummy.clearRequestParameters();
626 // Trying to decrease one more time
627 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.DECREASE);
628 waitForAssert(() -> {
629 assertFalse(radioServiceDummy.containsRequestParameter(0, VOLUME),
630 "We should be able to decrease the volume correctly");
631 radioServiceDummy.clearRequestParameters();
636 private void testChannelWithUPAndDOWNCommands(ChannelUID channelUID, Item item) {
637 synchronized (channelUID) {
638 // First we have to make sure that the item state is 0
639 radioHandler.handleCommand(channelUID, DecimalType.valueOf("0"));
640 waitForAssert(() -> {
641 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER),
642 "We should be able to turn on the radio");
643 radioServiceDummy.clearRequestParameters();
646 radioHandler.handleCommand(channelUID, UpDownType.UP);
647 waitForAssert(() -> {
648 assertTrue(radioServiceDummy.containsRequestParameter(1, VOLUME),
649 "We should be able to increase the volume correctly");
650 radioServiceDummy.clearRequestParameters();
653 radioHandler.handleCommand(channelUID, UpDownType.DOWN);
654 waitForAssert(() -> {
655 assertTrue(radioServiceDummy.containsRequestParameter(0, VOLUME),
656 "We should be able to decrease the volume correctly");
657 radioServiceDummy.clearRequestParameters();
660 // Trying to decrease one more time
661 radioHandler.handleCommand(channelUID, UpDownType.DOWN);
662 waitForAssert(() -> {
663 assertTrue(radioServiceDummy.areRequestParametersEmpty(),
664 "We shouldn't be able to decrease the volume below 0");
665 radioServiceDummy.clearRequestParameters();
671 * Verify the preset channel is updated.
674 public void presetChannelUpdated() {
675 String presetChannelID = FSInternetRadioBindingConstants.CHANNEL_PRESET;
676 String acceptedItemType = acceptedItemTypes.get(presetChannelID);
677 createChannel(DEFAULT_THING_UID, presetChannelID, acceptedItemType);
679 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
680 testRadioThingConsideringConfiguration(radioThing);
681 turnTheRadioOn(radioThing);
683 ChannelUID presetChannelUID = getChannelUID(radioThing, FSInternetRadioBindingConstants.CHANNEL_PRESET);
684 initializeItem(presetChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
686 radioHandler.handleCommand(presetChannelUID, DecimalType.valueOf("100"));
687 waitForAssert(() -> {
688 assertTrue(radioServiceDummy.containsRequestParameter(100, PRESET),
689 "We should be able to set value to the preset");
690 radioServiceDummy.clearRequestParameters();
695 * Verify the playInfoName channel is updated.
698 public void playInfoNameChannelUpdated() {
699 String playInfoNameChannelID = FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME;
700 String acceptedItemType = acceptedItemTypes.get(playInfoNameChannelID);
701 createChannel(DEFAULT_THING_UID, playInfoNameChannelID, acceptedItemType);
703 Thing radioThing = initializeRadioThingWithMockedHandler(DEFAULT_COMPLETE_CONFIGURATION);
704 testRadioThingConsideringConfiguration(radioThing);
706 turnTheRadioOn(radioThing);
708 ChannelUID playInfoNameChannelUID = getChannelUID(radioThing,
709 FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME);
710 initializeItem(playInfoNameChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
712 waitForAssert(() -> {
713 verifyOnlineStatusIsSet();
718 * Verify the playInfoText channel is updated.
721 public void playInfoTextChannelUpdated() {
722 String playInfoTextChannelID = FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT;
723 String acceptedItemType = acceptedItemTypes.get(playInfoTextChannelID);
724 createChannel(DEFAULT_THING_UID, playInfoTextChannelID, acceptedItemType);
726 Thing radioThing = initializeRadioThingWithMockedHandler(DEFAULT_COMPLETE_CONFIGURATION);
727 testRadioThingConsideringConfiguration(radioThing);
729 turnTheRadioOn(radioThing);
730 ChannelUID playInfoTextChannelUID = getChannelUID(radioThing,
731 FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT);
732 initializeItem(playInfoTextChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
734 waitForAssert(() -> {
735 verifyOnlineStatusIsSet();
739 private static Configuration createDefaultConfiguration() {
740 return createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, DEFAULT_CONFIG_PROPERTY_PIN,
741 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
744 private static Configuration createConfiguration(String ip, String pin, String port, String refresh) {
745 Configuration config = new Configuration();
746 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_IP, ip);
747 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PIN, pin);
748 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PORT, new BigDecimal(port));
749 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_REFRESH, new BigDecimal(refresh));
753 private static void setTheChannelsMap() {
754 acceptedItemTypes = new HashMap<>();
755 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_POWER, "Switch");
756 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_MODE, "Number");
757 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_MUTE, "Switch");
758 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME, "String");
759 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT, "String");
760 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PRESET, "Number");
761 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE, "Number");
762 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT, "Dimmer");
765 private void createThePowerChannel() {
766 String powerChannelID = FSInternetRadioBindingConstants.CHANNEL_POWER;
767 String acceptedItemType = acceptedItemTypes.get(powerChannelID);
768 powerChannel = createChannel(DEFAULT_THING_UID, powerChannelID, acceptedItemType);
771 private Item initializeItem(ChannelUID channelUID, String itemName, String acceptedItemType) {
774 switch (acceptedItemType) {
776 item = new NumberItem(itemName);
780 item = new StringItem(itemName);
784 item = new SwitchItem(itemName);
788 item = new DimmerItem(itemName);
795 private Channel createChannel(ThingUID thingUID, String channelID, String acceptedItemType) {
796 ChannelUID channelUID = new ChannelUID(thingUID, channelID);
798 Channel radioChannel = ChannelBuilder.create(channelUID, acceptedItemType).build();
799 channels.add(radioChannel);
803 private void testRadioThingConsideringConfiguration(Thing thing) {
804 Configuration config = thing.getConfiguration();
805 if (isConfigurationComplete(config)) {
806 waitForAssert(() -> {
807 verifyOnlineStatusIsSet();
810 waitForAssert(() -> {
811 verifyConfigurationError();
816 private boolean isConfigurationComplete(Configuration config) {
817 String ip = (String) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_IP);
818 BigDecimal port = (BigDecimal) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PORT.toString());
819 String pin = (String) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PIN.toString());
821 if (ip == null || port.compareTo(BigDecimal.ZERO) == 0 || StringUtils.isEmpty(pin)) {
827 @SuppressWarnings("null")
828 private Thing initializeRadioThing(Configuration config) {
829 radioThing = ThingBuilder.create(DEFAULT_THING_TYPE_UID, DEFAULT_THING_UID).withConfiguration(config)
830 .withChannels(channels).build();
832 callback = mock(ThingHandlerCallback.class);
834 radioHandler = new FSInternetRadioHandler(radioThing, httpClient);
835 radioHandler.setCallback(callback);
836 radioThing.setHandler(radioHandler);
837 radioThing.getHandler().initialize();
842 @SuppressWarnings("null")
843 private Thing initializeRadioThingWithMockedHandler(Configuration config) {
844 radioThing = ThingBuilder.create(DEFAULT_THING_TYPE_UID, DEFAULT_THING_UID).withConfiguration(config)
845 .withChannels(channels).build();
847 callback = mock(ThingHandlerCallback.class);
849 radioHandler = new MockedRadioHandler(radioThing, httpClient);
850 radioHandler.setCallback(callback);
851 radioThing.setHandler(radioHandler);
852 radioThing.getHandler().initialize();
857 private void turnTheRadioOn(Thing radioThing) {
858 radioHandler.handleCommand(getChannelUID(radioThing, FSInternetRadioBindingConstants.CHANNEL_POWER),
861 final FrontierSiliconRadio radio = HandlerUtils.getRadio(radioHandler);
863 waitForAssert(() -> {
865 assertTrue(radio.getPower());
866 } catch (IOException ex) {
867 throw new AssertionError("I/O error", ex);
872 private void verifyOnlineStatusIsSet() {
873 ThingStatusInfoBuilder statusBuilder = ThingStatusInfoBuilder.create(ThingStatus.ONLINE,
874 ThingStatusDetail.NONE);
875 ThingStatusInfo statusInfo = statusBuilder.withDescription(null).build();
876 verify(callback, atLeast(1)).statusUpdated(radioThing, statusInfo);
879 private void verifyConfigurationError() {
880 ThingStatusInfoBuilder statusBuilder = ThingStatusInfoBuilder.create(ThingStatus.OFFLINE,
881 ThingStatusDetail.CONFIGURATION_ERROR);
882 ThingStatusInfo statusInfo = statusBuilder.withDescription("Configuration incomplete").build();
883 verify(callback, atLeast(1)).statusUpdated(radioThing, statusInfo);
886 private void verifyCommunicationError(String exceptionMessage) {
887 ArgumentCaptor<ThingStatusInfo> captor = ArgumentCaptor.forClass(ThingStatusInfo.class);
888 verify(callback, atLeast(1)).statusUpdated(isA(Thing.class), captor.capture());
889 ThingStatusInfo status = captor.getValue();
890 assertThat(status.getStatus(), is(ThingStatus.OFFLINE));
891 assertThat(status.getStatusDetail(), is(ThingStatusDetail.COMMUNICATION_ERROR));
892 assertThat(status.getDescription().contains(exceptionMessage), is(true));