2 * Copyright (c) 2010-2023 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;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Objects;
30 import org.eclipse.jdt.annotation.NonNull;
31 import org.eclipse.jetty.client.HttpClient;
32 import org.eclipse.jetty.servlet.ServletHolder;
33 import org.junit.jupiter.api.AfterAll;
34 import org.junit.jupiter.api.BeforeAll;
35 import org.junit.jupiter.api.BeforeEach;
36 import org.junit.jupiter.api.Test;
37 import org.mockito.ArgumentCaptor;
38 import org.openhab.binding.fsinternetradio.internal.FSInternetRadioBindingConstants;
39 import org.openhab.binding.fsinternetradio.internal.handler.FSInternetRadioHandler;
40 import org.openhab.binding.fsinternetradio.internal.handler.HandlerUtils;
41 import org.openhab.binding.fsinternetradio.internal.radio.FrontierSiliconRadio;
42 import org.openhab.core.config.core.Configuration;
43 import org.openhab.core.items.Item;
44 import org.openhab.core.library.items.DimmerItem;
45 import org.openhab.core.library.items.NumberItem;
46 import org.openhab.core.library.items.StringItem;
47 import org.openhab.core.library.items.SwitchItem;
48 import org.openhab.core.library.types.DecimalType;
49 import org.openhab.core.library.types.IncreaseDecreaseType;
50 import org.openhab.core.library.types.OnOffType;
51 import org.openhab.core.library.types.PercentType;
52 import org.openhab.core.library.types.UpDownType;
53 import org.openhab.core.test.TestPortUtil;
54 import org.openhab.core.test.TestServer;
55 import org.openhab.core.test.java.JavaTest;
56 import org.openhab.core.thing.Channel;
57 import org.openhab.core.thing.ChannelUID;
58 import org.openhab.core.thing.Thing;
59 import org.openhab.core.thing.ThingStatus;
60 import org.openhab.core.thing.ThingStatusDetail;
61 import org.openhab.core.thing.ThingStatusInfo;
62 import org.openhab.core.thing.ThingTypeUID;
63 import org.openhab.core.thing.ThingUID;
64 import org.openhab.core.thing.binding.ThingHandlerCallback;
65 import org.openhab.core.thing.binding.builder.ChannelBuilder;
66 import org.openhab.core.thing.binding.builder.ThingBuilder;
67 import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
68 import org.openhab.core.types.UnDefType;
71 * OSGi tests for the {@link FSInternetRadioHandler}.
73 * @author Mihaela Memova - Initial contribution
74 * @author Markus Rathgeb - Migrated from Groovy to pure Java test, made more robust
75 * @author Velin Yordanov - Migrated to mockito
78 public class FSInternetRadioHandlerJavaTest extends JavaTest {
79 private static final String DEFAULT_TEST_THING_NAME = "testRadioThing";
80 private static final String DEFAULT_TEST_ITEM_NAME = "testItem";
81 private final String VOLUME = "volume";
83 // The request send for preset is "SET/netRemote.nav.action.selectPreset";
84 private static final String PRESET = "Preset";
85 private static final int TIMEOUT = 10 * 1000;
86 private static final ThingTypeUID DEFAULT_THING_TYPE_UID = FSInternetRadioBindingConstants.THING_TYPE_RADIO;
87 private static final ThingUID DEFAULT_THING_UID = new ThingUID(DEFAULT_THING_TYPE_UID, DEFAULT_TEST_THING_NAME);
88 private static final RadioServiceDummy radioServiceDummy = new RadioServiceDummy();
91 * In order to test a specific channel, it is necessary to create a Thing with two channels - CHANNEL_POWER
92 * and the tested channel. So before each test, the power channel is created and added
93 * to an ArrayList of channels. Then in the tests an additional channel is created and added to the ArrayList
96 private Channel powerChannel;
98 private ThingHandlerCallback callback;
100 private static TestServer server;
103 * A HashMap which saves all the 'channel-acceppted_item_type' pairs.
104 * It is set before all the tests.
106 private static Map<String, String> acceptedItemTypes;
109 * ArrayList of channels which is used to initialize a radioThing in the test cases.
111 private final List<Channel> channels = new ArrayList<>();
113 private FSInternetRadioHandler radioHandler;
114 private Thing radioThing;
116 private static HttpClient httpClient;
118 // default configuration properties
119 private static final String DEFAULT_CONFIG_PROPERTY_IP = "127.0.0.1";
120 private static final String DEFAULT_CONFIG_PROPERTY_PIN = "1234";
121 private static final int DEFAULT_CONFIG_PROPERTY_PORT = TestPortUtil.findFreePort();
123 /** The default refresh interval is 60 seconds. For the purposes of the tests it is set to 1 second */
124 private static final String DEFAULT_CONFIG_PROPERTY_REFRESH = "1";
125 private static final Configuration DEFAULT_COMPLETE_CONFIGURATION = createDefaultConfiguration();
128 public static void setUpClass() throws Exception {
129 ServletHolder holder = new ServletHolder(radioServiceDummy);
130 server = new TestServer(DEFAULT_CONFIG_PROPERTY_IP, DEFAULT_CONFIG_PROPERTY_PORT, TIMEOUT, holder);
132 server.startServer();
133 httpClient = new HttpClient();
138 public void setUp() {
139 createThePowerChannel();
143 public static void tearDownClass() throws Exception {
148 private static @NonNull Channel getChannel(final @NonNull Thing thing, final @NonNull String channelId) {
149 final Channel channel = thing.getChannel(channelId);
150 assertNotNull(channel);
151 Objects.requireNonNull(channel);
155 private static @NonNull ChannelUID getChannelUID(final @NonNull Thing thing, final @NonNull String channelId) {
156 final ChannelUID channelUID = getChannel(thing, channelId).getUID();
157 assertNotNull(channelUID);
162 * Verify OFFLINE Thing status when the IP is NULL.
165 public void offlineIfNullIp() {
166 Configuration config = createConfiguration(null, DEFAULT_CONFIG_PROPERTY_PIN,
167 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
168 Thing radioThingWithNullIP = initializeRadioThing(config);
169 testRadioThingConsideringConfiguration(radioThingWithNullIP);
173 * Verify OFFLINE Thing status when the PIN is empty String.
176 public void offlineIfEmptyPIN() {
177 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, "",
178 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
179 Thing radioThingWithEmptyPIN = initializeRadioThing(config);
180 testRadioThingConsideringConfiguration(radioThingWithEmptyPIN);
184 * Verify OFFLINE Thing status when the PORT is zero.
187 public void offlineIfZeroPort() {
188 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, DEFAULT_CONFIG_PROPERTY_PIN, "0",
189 DEFAULT_CONFIG_PROPERTY_REFRESH);
190 Thing radioThingWithZeroPort = initializeRadioThing(config);
191 testRadioThingConsideringConfiguration(radioThingWithZeroPort);
195 * Verify OFFLINE Thing status when the PIN is wrong.
198 public void offlineIfWrongPIN() {
199 final String wrongPin = "5678";
200 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, wrongPin,
201 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
202 initializeRadioThing(config);
203 waitForAssert(() -> {
204 String exceptionMessage = "Radio does not allow connection, maybe wrong pin?";
205 verifyCommunicationError(exceptionMessage);
210 * Verify OFFLINE Thing status when the HTTP response cannot be parsed correctly.
213 public void offlineIfParseError() {
214 // create a thing with two channels - the power channel and any of the others
215 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
216 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
217 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
219 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
220 testRadioThingConsideringConfiguration(radioThing);
222 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
225 * Setting the isInvalidResponseExpected variable to true
226 * in order to get the incorrect XML response from the servlet
228 radioServiceDummy.setInvalidResponse(true);
230 // try to handle a command
231 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
233 waitForAssert(() -> {
234 String exceptionMessage = "java.io.IOException: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 2;";
235 verifyCommunicationError(exceptionMessage);
237 radioServiceDummy.setInvalidResponse(false);
241 * Verify the HTTP status is handled correctly when it is not OK_200.
244 public void httpStatusNokHandling() {
245 // create a thing with two channels - the power channel and any of the others
246 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
247 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
248 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
250 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
251 testRadioThingConsideringConfiguration(radioThing);
254 turnTheRadioOn(radioThing);
257 * Setting the needed boolean variable to false, so we can be sure
258 * that the XML response won't have an OK_200 status
260 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
261 Item modeTestItem = initializeItem(modeChannelUID, CHANNEL_MODE, acceptedItemType);
263 // try to handle a command
264 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
266 waitForAssert(() -> {
267 assertSame(UnDefType.NULL, modeTestItem.getState());
272 * Verify ONLINE status of a Thing with complete configuration.
275 public void verifyOnlineStatus() {
276 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
277 testRadioThingConsideringConfiguration(radioThing);
281 * Verify the power channel is updated.
284 public void powerChannelUpdated() {
285 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
286 testRadioThingConsideringConfiguration(radioThing);
288 ChannelUID powerChannelUID = powerChannel.getUID();
289 initializeItem(powerChannelUID, DEFAULT_TEST_ITEM_NAME,
290 acceptedItemTypes.get(FSInternetRadioBindingConstants.CHANNEL_POWER));
292 radioHandler.handleCommand(powerChannelUID, OnOffType.ON);
293 waitForAssert(() -> {
294 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER),
295 "We should be able to turn on the radio");
296 radioServiceDummy.clearRequestParameters();
299 radioHandler.handleCommand(powerChannelUID, OnOffType.OFF);
300 waitForAssert(() -> {
301 assertTrue(radioServiceDummy.containsRequestParameter(0, CHANNEL_POWER),
302 "We should be able to turn off the radio");
303 radioServiceDummy.clearRequestParameters();
307 * Setting the needed boolean variable to true, so we can be sure
308 * that an invalid value will be returned in the XML response
310 radioHandler.handleCommand(powerChannelUID, OnOffType.ON);
311 waitForAssert(() -> {
312 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER),
313 "We should be able to turn on the radio");
314 radioServiceDummy.clearRequestParameters();
319 * Verify the mute channel is updated.
322 public void muteChhannelUpdated() {
323 String muteChannelID = FSInternetRadioBindingConstants.CHANNEL_MUTE;
324 String acceptedItemType = acceptedItemTypes.get(muteChannelID);
325 createChannel(DEFAULT_THING_UID, muteChannelID, acceptedItemType);
327 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
328 testRadioThingConsideringConfiguration(radioThing);
330 turnTheRadioOn(radioThing);
332 ChannelUID muteChannelUID = getChannelUID(radioThing, FSInternetRadioBindingConstants.CHANNEL_MUTE);
333 initializeItem(muteChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
335 radioHandler.handleCommand(muteChannelUID, OnOffType.ON);
336 waitForAssert(() -> {
337 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_MUTE),
338 "We should be able to mute the radio");
339 radioServiceDummy.clearRequestParameters();
342 radioHandler.handleCommand(muteChannelUID, OnOffType.OFF);
343 waitForAssert(() -> {
344 assertTrue(radioServiceDummy.containsRequestParameter(0, CHANNEL_MUTE),
345 "We should be able to unmute the radio");
346 radioServiceDummy.clearRequestParameters();
350 * Setting the needed boolean variable to true, so we can be sure
351 * that an invalid value will be returned in the XML response
356 * Verify the mode channel is updated.
359 public void modeChannelUdpated() {
360 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
361 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
362 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
364 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
365 testRadioThingConsideringConfiguration(radioThing);
367 turnTheRadioOn(radioThing);
369 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
370 initializeItem(modeChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
372 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
373 waitForAssert(() -> {
374 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_MODE),
375 "We should be able to update the mode channel correctly");
376 radioServiceDummy.clearRequestParameters();
380 * Setting the needed boolean variable to true, so we can be sure
381 * that an invalid value will be returned in the XML response
383 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("3"));
384 waitForAssert(() -> {
385 assertTrue(radioServiceDummy.containsRequestParameter(3, CHANNEL_MODE),
386 "We should be able to update the mode channel correctly");
387 radioServiceDummy.clearRequestParameters();
392 * Verify the volume is updated through the CHANNEL_VOLUME_ABSOLUTE using INCREASE and DECREASE commands.
395 public void volumechannelUpdatedAbsIncDec() {
396 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
397 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
398 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
400 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
401 testRadioThingConsideringConfiguration(radioThing);
403 turnTheRadioOn(radioThing);
405 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
406 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
407 absoluteAcceptedItemType);
409 testChannelWithINCREASEAndDECREASECommands(absoluteVolumeChannelUID, volumeTestItem);
413 * Verify the volume is updated through the CHANNEL_VOLUME_ABSOLUTE using UP and DOWN commands.
416 public void volumeChannelUpdatedAbsUpDown() {
417 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
418 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
419 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
421 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
422 testRadioThingConsideringConfiguration(radioThing);
424 turnTheRadioOn(radioThing);
426 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
427 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
428 absoluteAcceptedItemType);
430 testChannelWithUPAndDOWNCommands(absoluteVolumeChannelUID, volumeTestItem);
434 * Verify the invalid values when updating CHANNEL_VOLUME_ABSOLUTE are handled correctly.
437 public void invalidAbsVolumeValues() {
438 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
439 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
440 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
442 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
443 testRadioThingConsideringConfiguration(radioThing);
445 turnTheRadioOn(radioThing);
447 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
448 initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME, absoluteAcceptedItemType);
450 // Trying to set a value that is greater than the maximum volume
451 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("36"));
453 waitForAssert(() -> {
454 assertTrue(radioServiceDummy.containsRequestParameter(32, VOLUME),
455 "The volume should not exceed the maximum value");
456 radioServiceDummy.clearRequestParameters();
459 // Trying to increase the volume more than its maximum value using the INCREASE command
460 radioHandler.handleCommand(absoluteVolumeChannelUID, IncreaseDecreaseType.INCREASE);
462 waitForAssert(() -> {
463 assertTrue(radioServiceDummy.areRequestParametersEmpty(),
464 "The volume should not be increased above the maximum value");
465 radioServiceDummy.clearRequestParameters();
468 // Trying to increase the volume more than its maximum value using the UP command
469 radioHandler.handleCommand(absoluteVolumeChannelUID, UpDownType.UP);
471 waitForAssert(() -> {
472 assertTrue(radioServiceDummy.areRequestParametersEmpty(),
473 "The volume should not be increased above the maximum value");
474 radioServiceDummy.clearRequestParameters();
477 // Trying to set a value that is lower than the minimum volume value
478 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("-10"));
479 waitForAssert(() -> {
480 assertTrue(radioServiceDummy.containsRequestParameter(0, VOLUME),
481 "The volume should not be decreased below 0");
482 radioServiceDummy.clearRequestParameters();
486 * Setting the needed boolean variable to true, so we can be sure
487 * that an invalid value will be returned in the XML response
490 // trying to set the volume
491 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("15"));
492 waitForAssert(() -> {
493 assertTrue(radioServiceDummy.containsRequestParameter(15, VOLUME),
494 "We should be able to set the volume correctly");
495 radioServiceDummy.clearRequestParameters();
500 * Verify the volume is updated through the CHANNEL_VOLUME_PERCENT using INCREASE and DECREASE commands.
503 public void volumeChannelUpdatedPercIncDec() {
505 * The volume is set through the CHANNEL_VOLUME_PERCENT in order to check if
506 * the absolute volume will be updated properly.
508 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
509 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
510 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
512 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
513 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
514 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
516 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
517 testRadioThingConsideringConfiguration(radioThing);
519 turnTheRadioOn(radioThing);
521 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
522 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
523 absoluteAcceptedItemType);
525 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
527 testChannelWithINCREASEAndDECREASECommands(percentVolumeChannelUID, volumeTestItem);
531 * Verify the volume is updated through the CHANNEL_VOLUME_PERCENT using UP and DOWN commands.
534 public void volumeChannelUpdatedPercUpDown() {
536 * The volume is set through the CHANNEL_VOLUME_PERCENT in order to check if
537 * the absolute volume will be updated properly.
539 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
540 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
541 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
543 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
544 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
545 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
547 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
548 testRadioThingConsideringConfiguration(radioThing);
550 turnTheRadioOn(radioThing);
552 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
553 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
554 absoluteAcceptedItemType);
556 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
558 testChannelWithUPAndDOWNCommands(percentVolumeChannelUID, volumeTestItem);
562 * Verify the valid and invalid values when updating CHANNEL_VOLUME_PERCENT are handled correctly.
565 public void validInvalidPercVolume() {
566 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
567 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
568 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
570 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
571 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
572 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
574 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
575 testRadioThingConsideringConfiguration(radioThing);
577 turnTheRadioOn(radioThing);
579 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
580 initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME, absoluteAcceptedItemType);
582 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
585 * Giving the handler a valid percent value. According to the FrontierSiliconRadio's
586 * documentation 100 percents correspond to 32 absolute value
588 radioHandler.handleCommand(percentVolumeChannelUID, PercentType.valueOf("50"));
589 waitForAssert(() -> {
590 assertTrue(radioServiceDummy.containsRequestParameter(16, VOLUME),
591 "We should be able to set the volume correctly using percentages.");
592 radioServiceDummy.clearRequestParameters();
595 radioHandler.handleCommand(percentVolumeChannelUID, PercentType.valueOf("15"));
597 waitForAssert(() -> {
598 assertTrue(radioServiceDummy.containsRequestParameter(4, VOLUME),
599 "We should be able to set the volume correctly using percentages.");
600 radioServiceDummy.clearRequestParameters();
604 private void testChannelWithINCREASEAndDECREASECommands(ChannelUID channelUID, Item item) {
605 synchronized (channelUID) {
606 // First we have to make sure that the item state is 0
607 radioHandler.handleCommand(channelUID, DecimalType.valueOf("0"));
608 waitForAssert(() -> {
609 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER),
610 "We should be able to turn on the radio");
611 radioServiceDummy.clearRequestParameters();
614 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.INCREASE);
616 waitForAssert(() -> {
617 assertTrue(radioServiceDummy.containsRequestParameter(1, VOLUME),
618 "We should be able to increase the volume correctly");
619 radioServiceDummy.clearRequestParameters();
622 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.DECREASE);
623 waitForAssert(() -> {
624 assertTrue(radioServiceDummy.containsRequestParameter(0, VOLUME),
625 "We should be able to increase the volume correctly");
626 radioServiceDummy.clearRequestParameters();
629 // Trying to decrease one more time
630 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.DECREASE);
631 waitForAssert(() -> {
632 assertFalse(radioServiceDummy.containsRequestParameter(0, VOLUME),
633 "We should be able to decrease the volume correctly");
634 radioServiceDummy.clearRequestParameters();
639 private void testChannelWithUPAndDOWNCommands(ChannelUID channelUID, Item item) {
640 synchronized (channelUID) {
641 // First we have to make sure that the item state is 0
642 radioHandler.handleCommand(channelUID, DecimalType.valueOf("0"));
643 waitForAssert(() -> {
644 assertTrue(radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER),
645 "We should be able to turn on the radio");
646 radioServiceDummy.clearRequestParameters();
649 radioHandler.handleCommand(channelUID, UpDownType.UP);
650 waitForAssert(() -> {
651 assertTrue(radioServiceDummy.containsRequestParameter(1, VOLUME),
652 "We should be able to increase the volume correctly");
653 radioServiceDummy.clearRequestParameters();
656 radioHandler.handleCommand(channelUID, UpDownType.DOWN);
657 waitForAssert(() -> {
658 assertTrue(radioServiceDummy.containsRequestParameter(0, VOLUME),
659 "We should be able to decrease the volume correctly");
660 radioServiceDummy.clearRequestParameters();
663 // Trying to decrease one more time
664 radioHandler.handleCommand(channelUID, UpDownType.DOWN);
665 waitForAssert(() -> {
666 assertTrue(radioServiceDummy.areRequestParametersEmpty(),
667 "We shouldn't be able to decrease the volume below 0");
668 radioServiceDummy.clearRequestParameters();
674 * Verify the preset channel is updated.
677 public void presetChannelUpdated() {
678 String presetChannelID = FSInternetRadioBindingConstants.CHANNEL_PRESET;
679 String acceptedItemType = acceptedItemTypes.get(presetChannelID);
680 createChannel(DEFAULT_THING_UID, presetChannelID, acceptedItemType);
682 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
683 testRadioThingConsideringConfiguration(radioThing);
684 turnTheRadioOn(radioThing);
686 ChannelUID presetChannelUID = getChannelUID(radioThing, FSInternetRadioBindingConstants.CHANNEL_PRESET);
687 initializeItem(presetChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
689 radioHandler.handleCommand(presetChannelUID, DecimalType.valueOf("100"));
690 waitForAssert(() -> {
691 assertTrue(radioServiceDummy.containsRequestParameter(100, PRESET),
692 "We should be able to set value to the preset");
693 radioServiceDummy.clearRequestParameters();
698 * Verify the playInfoName channel is updated.
701 public void playInfoNameChannelUpdated() {
702 String playInfoNameChannelID = FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME;
703 String acceptedItemType = acceptedItemTypes.get(playInfoNameChannelID);
704 createChannel(DEFAULT_THING_UID, playInfoNameChannelID, acceptedItemType);
706 Thing radioThing = initializeRadioThingWithMockedHandler(DEFAULT_COMPLETE_CONFIGURATION);
707 testRadioThingConsideringConfiguration(radioThing);
709 turnTheRadioOn(radioThing);
711 ChannelUID playInfoNameChannelUID = getChannelUID(radioThing,
712 FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME);
713 initializeItem(playInfoNameChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
715 waitForAssert(() -> {
716 verifyOnlineStatusIsSet();
721 * Verify the playInfoText channel is updated.
724 public void playInfoTextChannelUpdated() {
725 String playInfoTextChannelID = FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT;
726 String acceptedItemType = acceptedItemTypes.get(playInfoTextChannelID);
727 createChannel(DEFAULT_THING_UID, playInfoTextChannelID, acceptedItemType);
729 Thing radioThing = initializeRadioThingWithMockedHandler(DEFAULT_COMPLETE_CONFIGURATION);
730 testRadioThingConsideringConfiguration(radioThing);
732 turnTheRadioOn(radioThing);
733 ChannelUID playInfoTextChannelUID = getChannelUID(radioThing,
734 FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT);
735 initializeItem(playInfoTextChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
737 waitForAssert(() -> {
738 verifyOnlineStatusIsSet();
742 private static Configuration createDefaultConfiguration() {
743 return createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, DEFAULT_CONFIG_PROPERTY_PIN,
744 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
747 private static Configuration createConfiguration(String ip, String pin, String port, String refresh) {
748 Configuration config = new Configuration();
749 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_IP, ip);
750 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PIN, pin);
751 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PORT, new BigDecimal(port));
752 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_REFRESH, new BigDecimal(refresh));
756 private static void setTheChannelsMap() {
757 acceptedItemTypes = new HashMap<>();
758 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_POWER, "Switch");
759 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_MODE, "Number");
760 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_MUTE, "Switch");
761 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME, "String");
762 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT, "String");
763 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PRESET, "Number");
764 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE, "Number");
765 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT, "Dimmer");
768 private void createThePowerChannel() {
769 String powerChannelID = FSInternetRadioBindingConstants.CHANNEL_POWER;
770 String acceptedItemType = acceptedItemTypes.get(powerChannelID);
771 powerChannel = createChannel(DEFAULT_THING_UID, powerChannelID, acceptedItemType);
774 private Item initializeItem(ChannelUID channelUID, String itemName, String acceptedItemType) {
777 switch (acceptedItemType) {
779 item = new NumberItem(itemName);
783 item = new StringItem(itemName);
787 item = new SwitchItem(itemName);
791 item = new DimmerItem(itemName);
798 private Channel createChannel(ThingUID thingUID, String channelID, String acceptedItemType) {
799 ChannelUID channelUID = new ChannelUID(thingUID, channelID);
801 Channel radioChannel = ChannelBuilder.create(channelUID, acceptedItemType).build();
802 channels.add(radioChannel);
806 private void testRadioThingConsideringConfiguration(Thing thing) {
807 Configuration config = thing.getConfiguration();
808 if (isConfigurationComplete(config)) {
809 waitForAssert(() -> {
810 verifyOnlineStatusIsSet();
813 waitForAssert(() -> {
814 verifyConfigurationError();
819 private boolean isConfigurationComplete(Configuration config) {
820 String ip = (String) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_IP);
821 BigDecimal port = (BigDecimal) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PORT.toString());
822 String pin = (String) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PIN.toString());
824 return !(ip == null || port.compareTo(BigDecimal.ZERO) == 0 || pin == null || pin.isEmpty());
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));