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.junit.Assert.*;
17 import static org.mockito.ArgumentMatchers.isA;
18 import static org.mockito.Mockito.*;
19 import static org.openhab.binding.fsinternetradio.internal.FSInternetRadioBindingConstants.*;
21 import java.io.IOException;
22 import java.math.BigDecimal;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
28 import org.apache.commons.lang.StringUtils;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.eclipse.jetty.client.HttpClient;
31 import org.eclipse.jetty.servlet.ServletHolder;
32 import org.junit.AfterClass;
33 import org.junit.Assert;
34 import org.junit.Before;
35 import org.junit.BeforeClass;
36 import org.junit.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 Assert.assertNotNull(channel);
154 private static @NonNull ChannelUID getChannelUID(final @NonNull Thing thing, final @NonNull String channelId) {
155 final ChannelUID channelUID = getChannel(thing, channelId).getUID();
156 Assert.assertNotNull(channelUID);
161 * Verify OFFLINE Thing status when the IP is NULL.
164 public void offlineIfNullIp() {
165 Configuration config = createConfiguration(null, DEFAULT_CONFIG_PROPERTY_PIN,
166 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
167 Thing radioThingWithNullIP = initializeRadioThing(config);
168 testRadioThingConsideringConfiguration(radioThingWithNullIP);
172 * Verify OFFLINE Thing status when the PIN is empty String.
175 public void offlineIfEmptyPIN() {
176 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, "",
177 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
178 Thing radioThingWithEmptyPIN = initializeRadioThing(config);
179 testRadioThingConsideringConfiguration(radioThingWithEmptyPIN);
183 * Verify OFFLINE Thing status when the PORT is zero.
186 public void offlineIfZeroPort() {
187 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, DEFAULT_CONFIG_PROPERTY_PIN, "0",
188 DEFAULT_CONFIG_PROPERTY_REFRESH);
189 Thing radioThingWithZeroPort = initializeRadioThing(config);
190 testRadioThingConsideringConfiguration(radioThingWithZeroPort);
194 * Verify OFFLINE Thing status when the PIN is wrong.
197 public void offlineIfWrongPIN() {
198 final String wrongPin = "5678";
199 Configuration config = createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, wrongPin,
200 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
201 initializeRadioThing(config);
202 waitForAssert(() -> {
203 String exceptionMessage = "Radio does not allow connection, maybe wrong pin?";
204 verifyCommunicationError(exceptionMessage);
209 * Verify OFFLINE Thing status when the HTTP response cannot be parsed correctly.
212 public void offlineIfParseError() {
213 // create a thing with two channels - the power channel and any of the others
214 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
215 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
216 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
218 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
219 testRadioThingConsideringConfiguration(radioThing);
221 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
224 * Setting the isInvalidResponseExpected variable to true
225 * in order to get the incorrect XML response from the servlet
227 radioServiceDummy.setInvalidResponse(true);
229 // try to handle a command
230 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
232 waitForAssert(() -> {
233 String exceptionMessage = "java.io.IOException: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 2;";
234 verifyCommunicationError(exceptionMessage);
236 radioServiceDummy.setInvalidResponse(false);
240 * Verify the HTTP status is handled correctly when it is not OK_200.
243 public void httpStatusNokHandling() {
244 // create a thing with two channels - the power channel and any of the others
245 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
246 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
247 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
249 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
250 testRadioThingConsideringConfiguration(radioThing);
253 turnTheRadioOn(radioThing);
256 * Setting the needed boolean variable to false, so we can be sure
257 * that the XML response won't have a OK_200 status
259 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
260 Item modeTestItem = initializeItem(modeChannelUID, CHANNEL_MODE, acceptedItemType);
262 // try to handle a command
263 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
265 waitForAssert(() -> {
266 assertSame(UnDefType.NULL, modeTestItem.getState());
271 * Verify ONLINE status of a Thing with complete configuration.
274 public void verifyOnlineStatus() {
275 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
276 testRadioThingConsideringConfiguration(radioThing);
280 * Verify the power channel is updated.
283 public void powerChannelUpdated() {
284 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
285 testRadioThingConsideringConfiguration(radioThing);
287 ChannelUID powerChannelUID = powerChannel.getUID();
288 initializeItem(powerChannelUID, DEFAULT_TEST_ITEM_NAME,
289 acceptedItemTypes.get(FSInternetRadioBindingConstants.CHANNEL_POWER));
291 radioHandler.handleCommand(powerChannelUID, OnOffType.ON);
292 waitForAssert(() -> {
293 assertTrue("We should be able to turn on the radio",
294 radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER));
295 radioServiceDummy.clearRequestParameters();
298 radioHandler.handleCommand(powerChannelUID, OnOffType.OFF);
299 waitForAssert(() -> {
300 assertTrue("We should be able to turn off the radio",
301 radioServiceDummy.containsRequestParameter(0, CHANNEL_POWER));
302 radioServiceDummy.clearRequestParameters();
306 * Setting the needed boolean variable to true, so we can be sure
307 * that an invalid value will be returned in the XML response
309 radioHandler.handleCommand(powerChannelUID, OnOffType.ON);
310 waitForAssert(() -> {
311 assertTrue("We should be able to turn on the radio",
312 radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER));
313 radioServiceDummy.clearRequestParameters();
318 * Verify the mute channel is updated.
321 public void muteChhannelUpdated() {
322 String muteChannelID = FSInternetRadioBindingConstants.CHANNEL_MUTE;
323 String acceptedItemType = acceptedItemTypes.get(muteChannelID);
324 createChannel(DEFAULT_THING_UID, muteChannelID, acceptedItemType);
326 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
327 testRadioThingConsideringConfiguration(radioThing);
329 turnTheRadioOn(radioThing);
331 ChannelUID muteChannelUID = getChannelUID(radioThing, FSInternetRadioBindingConstants.CHANNEL_MUTE);
332 initializeItem(muteChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
334 radioHandler.handleCommand(muteChannelUID, OnOffType.ON);
335 waitForAssert(() -> {
336 assertTrue("We should be able to mute the radio",
337 radioServiceDummy.containsRequestParameter(1, CHANNEL_MUTE));
338 radioServiceDummy.clearRequestParameters();
341 radioHandler.handleCommand(muteChannelUID, OnOffType.OFF);
342 waitForAssert(() -> {
343 assertTrue("We should be able to unmute the radio",
344 radioServiceDummy.containsRequestParameter(0, CHANNEL_MUTE));
345 radioServiceDummy.clearRequestParameters();
349 * Setting the needed boolean variable to true, so we can be sure
350 * that an invalid value will be returned in the XML response
355 * Verify the mode channel is updated.
358 public void modeChannelUdpated() {
359 String modeChannelID = FSInternetRadioBindingConstants.CHANNEL_MODE;
360 String acceptedItemType = acceptedItemTypes.get(modeChannelID);
361 createChannel(DEFAULT_THING_UID, modeChannelID, acceptedItemType);
363 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
364 testRadioThingConsideringConfiguration(radioThing);
366 turnTheRadioOn(radioThing);
368 ChannelUID modeChannelUID = getChannelUID(radioThing, modeChannelID);
369 initializeItem(modeChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
371 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("1"));
372 waitForAssert(() -> {
373 assertTrue("We should be able to update the mode channel correctly",
374 radioServiceDummy.containsRequestParameter(1, CHANNEL_MODE));
375 radioServiceDummy.clearRequestParameters();
379 * Setting the needed boolean variable to true, so we can be sure
380 * that an invalid value will be returned in the XML response
382 radioHandler.handleCommand(modeChannelUID, DecimalType.valueOf("3"));
383 waitForAssert(() -> {
384 assertTrue("We should be able to update the mode channel correctly",
385 radioServiceDummy.containsRequestParameter(3, CHANNEL_MODE));
386 radioServiceDummy.clearRequestParameters();
391 * Verify the volume is updated through the CHANNEL_VOLUME_ABSOLUTE using INCREASE and DECREASE commands.
394 public void volumechannelUpdatedAbsIncDec() {
395 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
396 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
397 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
399 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
400 testRadioThingConsideringConfiguration(radioThing);
402 turnTheRadioOn(radioThing);
404 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
405 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
406 absoluteAcceptedItemType);
408 testChannelWithINCREASEAndDECREASECommands(absoluteVolumeChannelUID, volumeTestItem);
412 * Verify the volume is updated through the CHANNEL_VOLUME_ABSOLUTE using UP and DOWN commands.
415 public void volumeChannelUpdatedAbsUpDown() {
416 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
417 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
418 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
420 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
421 testRadioThingConsideringConfiguration(radioThing);
423 turnTheRadioOn(radioThing);
425 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
426 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
427 absoluteAcceptedItemType);
429 testChannelWithUPAndDOWNCommands(absoluteVolumeChannelUID, volumeTestItem);
433 * Verify the invalid values when updating CHANNEL_VOLUME_ABSOLUTE are handled correctly.
436 public void invalidAbsVolumeValues() {
437 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
438 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
439 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
441 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
442 testRadioThingConsideringConfiguration(radioThing);
444 turnTheRadioOn(radioThing);
446 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
447 initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME, absoluteAcceptedItemType);
449 // Trying to set a value that is greater than the maximum volume
450 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("36"));
452 waitForAssert(() -> {
453 assertTrue("The volume should not exceed the maximum value",
454 radioServiceDummy.containsRequestParameter(32, VOLUME));
455 radioServiceDummy.clearRequestParameters();
458 // Trying to increase the volume more than its maximum value using the INCREASE command
459 radioHandler.handleCommand(absoluteVolumeChannelUID, IncreaseDecreaseType.INCREASE);
461 waitForAssert(() -> {
462 assertTrue("The volume should not be increased above the maximum value",
463 radioServiceDummy.areRequestParametersEmpty());
464 radioServiceDummy.clearRequestParameters();
467 // Trying to increase the volume more than its maximum value using the UP command
468 radioHandler.handleCommand(absoluteVolumeChannelUID, UpDownType.UP);
470 waitForAssert(() -> {
471 assertTrue("The volume should not be increased above the maximum value",
472 radioServiceDummy.areRequestParametersEmpty());
473 radioServiceDummy.clearRequestParameters();
476 // Trying to set a value that is lower than the minimum volume value
477 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("-10"));
478 waitForAssert(() -> {
479 assertTrue("The volume should not be decreased below 0",
480 radioServiceDummy.containsRequestParameter(0, VOLUME));
481 radioServiceDummy.clearRequestParameters();
485 * Setting the needed boolean variable to true, so we can be sure
486 * that an invalid value will be returned in the XML response
489 // trying to set the volume
490 radioHandler.handleCommand(absoluteVolumeChannelUID, DecimalType.valueOf("15"));
491 waitForAssert(() -> {
492 assertTrue("We should be able to set the volume correctly",
493 radioServiceDummy.containsRequestParameter(15, VOLUME));
494 radioServiceDummy.clearRequestParameters();
499 * Verify the volume is updated through the CHANNEL_VOLUME_PERCENT using INCREASE and DECREASE commands.
502 public void volumeChannelUpdatedPercIncDec() {
504 * The volume is set through the CHANNEL_VOLUME_PERCENT in order to check if
505 * the absolute volume will be updated properly.
507 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
508 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
509 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
511 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
512 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
513 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
515 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
516 testRadioThingConsideringConfiguration(radioThing);
518 turnTheRadioOn(radioThing);
520 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
521 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
522 absoluteAcceptedItemType);
524 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
526 testChannelWithINCREASEAndDECREASECommands(percentVolumeChannelUID, volumeTestItem);
530 * Verify the volume is updated through the CHANNEL_VOLUME_PERCENT using UP and DOWN commands.
533 public void volumeChannelUpdatedPercUpDown() {
535 * The volume is set through the CHANNEL_VOLUME_PERCENT in order to check if
536 * the absolute volume will be updated properly.
538 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
539 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
540 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
542 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
543 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
544 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
546 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
547 testRadioThingConsideringConfiguration(radioThing);
549 turnTheRadioOn(radioThing);
551 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
552 Item volumeTestItem = initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME,
553 absoluteAcceptedItemType);
555 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
557 testChannelWithUPAndDOWNCommands(percentVolumeChannelUID, volumeTestItem);
561 * Verify the valid and invalid values when updating CHANNEL_VOLUME_PERCENT are handled correctly.
564 public void validInvalidPercVolume() {
565 String absoluteVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE;
566 String absoluteAcceptedItemType = acceptedItemTypes.get(absoluteVolumeChannelID);
567 createChannel(DEFAULT_THING_UID, absoluteVolumeChannelID, absoluteAcceptedItemType);
569 String percentVolumeChannelID = FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT;
570 String percentAcceptedItemType = acceptedItemTypes.get(percentVolumeChannelID);
571 createChannel(DEFAULT_THING_UID, percentVolumeChannelID, percentAcceptedItemType);
573 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
574 testRadioThingConsideringConfiguration(radioThing);
576 turnTheRadioOn(radioThing);
578 ChannelUID absoluteVolumeChannelUID = getChannelUID(radioThing, absoluteVolumeChannelID);
579 initializeItem(absoluteVolumeChannelUID, DEFAULT_TEST_ITEM_NAME, absoluteAcceptedItemType);
581 ChannelUID percentVolumeChannelUID = getChannelUID(radioThing, percentVolumeChannelID);
584 * Giving the handler a valid percent value. According to the FrontierSiliconRadio's
585 * documentation 100 percents correspond to 32 absolute value
587 radioHandler.handleCommand(percentVolumeChannelUID, PercentType.valueOf("50"));
588 waitForAssert(() -> {
589 assertTrue("We should be able to set the volume correctly using percentages.",
590 radioServiceDummy.containsRequestParameter(16, VOLUME));
591 radioServiceDummy.clearRequestParameters();
594 radioHandler.handleCommand(percentVolumeChannelUID, PercentType.valueOf("15"));
596 waitForAssert(() -> {
597 assertTrue("We should be able to set the volume correctly using percentages.",
598 radioServiceDummy.containsRequestParameter(4, VOLUME));
599 radioServiceDummy.clearRequestParameters();
603 private void testChannelWithINCREASEAndDECREASECommands(ChannelUID channelUID, Item item) {
604 synchronized (channelUID) {
605 // First we have to make sure that the item state is 0
606 radioHandler.handleCommand(channelUID, DecimalType.valueOf("0"));
607 waitForAssert(() -> {
608 assertTrue("We should be able to turn on the radio",
609 radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER));
610 radioServiceDummy.clearRequestParameters();
613 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.INCREASE);
615 waitForAssert(() -> {
616 assertTrue("We should be able to increase the volume correctly",
617 radioServiceDummy.containsRequestParameter(1, VOLUME));
618 radioServiceDummy.clearRequestParameters();
621 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.DECREASE);
622 waitForAssert(() -> {
623 assertTrue("We should be able to increase the volume correctly",
624 radioServiceDummy.containsRequestParameter(0, VOLUME));
625 radioServiceDummy.clearRequestParameters();
628 // Trying to decrease one more time
629 radioHandler.handleCommand(channelUID, IncreaseDecreaseType.DECREASE);
630 waitForAssert(() -> {
631 assertFalse("We should be able to decrease the volume correctly",
632 radioServiceDummy.containsRequestParameter(0, VOLUME));
633 radioServiceDummy.clearRequestParameters();
638 private void testChannelWithUPAndDOWNCommands(ChannelUID channelUID, Item item) {
639 synchronized (channelUID) {
640 // First we have to make sure that the item state is 0
641 radioHandler.handleCommand(channelUID, DecimalType.valueOf("0"));
642 waitForAssert(() -> {
643 assertTrue("We should be able to turn on the radio",
644 radioServiceDummy.containsRequestParameter(1, CHANNEL_POWER));
645 radioServiceDummy.clearRequestParameters();
648 radioHandler.handleCommand(channelUID, UpDownType.UP);
649 waitForAssert(() -> {
650 assertTrue("We should be able to increase the volume correctly",
651 radioServiceDummy.containsRequestParameter(1, VOLUME));
652 radioServiceDummy.clearRequestParameters();
655 radioHandler.handleCommand(channelUID, UpDownType.DOWN);
656 waitForAssert(() -> {
657 assertTrue("We should be able to decrease the volume correctly",
658 radioServiceDummy.containsRequestParameter(0, VOLUME));
659 radioServiceDummy.clearRequestParameters();
662 // Trying to decrease one more time
663 radioHandler.handleCommand(channelUID, UpDownType.DOWN);
664 waitForAssert(() -> {
665 assertTrue("We shouldn't be able to decrease the volume below 0",
666 radioServiceDummy.areRequestParametersEmpty());
667 radioServiceDummy.clearRequestParameters();
673 * Verify the preset channel is updated.
676 public void presetChannelUpdated() {
677 String presetChannelID = FSInternetRadioBindingConstants.CHANNEL_PRESET;
678 String acceptedItemType = acceptedItemTypes.get(presetChannelID);
679 createChannel(DEFAULT_THING_UID, presetChannelID, acceptedItemType);
681 Thing radioThing = initializeRadioThing(DEFAULT_COMPLETE_CONFIGURATION);
682 testRadioThingConsideringConfiguration(radioThing);
683 turnTheRadioOn(radioThing);
685 ChannelUID presetChannelUID = getChannelUID(radioThing, FSInternetRadioBindingConstants.CHANNEL_PRESET);
686 initializeItem(presetChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
688 radioHandler.handleCommand(presetChannelUID, DecimalType.valueOf("100"));
689 waitForAssert(() -> {
690 assertTrue("We should be able to set value to the preset",
691 radioServiceDummy.containsRequestParameter(100, PRESET));
692 radioServiceDummy.clearRequestParameters();
697 * Verify the playInfoName channel is updated.
700 public void playInfoNameChannelUpdated() {
701 String playInfoNameChannelID = FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME;
702 String acceptedItemType = acceptedItemTypes.get(playInfoNameChannelID);
703 createChannel(DEFAULT_THING_UID, playInfoNameChannelID, acceptedItemType);
705 Thing radioThing = initializeRadioThingWithMockedHandler(DEFAULT_COMPLETE_CONFIGURATION);
706 testRadioThingConsideringConfiguration(radioThing);
708 turnTheRadioOn(radioThing);
710 ChannelUID playInfoNameChannelUID = getChannelUID(radioThing,
711 FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME);
712 initializeItem(playInfoNameChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
714 waitForAssert(() -> {
715 verifyOnlineStatusIsSet();
720 * Verify the playInfoText channel is updated.
723 public void playInfoTextChannelUpdated() {
724 String playInfoTextChannelID = FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT;
725 String acceptedItemType = acceptedItemTypes.get(playInfoTextChannelID);
726 createChannel(DEFAULT_THING_UID, playInfoTextChannelID, acceptedItemType);
728 Thing radioThing = initializeRadioThingWithMockedHandler(DEFAULT_COMPLETE_CONFIGURATION);
729 testRadioThingConsideringConfiguration(radioThing);
731 turnTheRadioOn(radioThing);
732 ChannelUID playInfoTextChannelUID = getChannelUID(radioThing,
733 FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT);
734 initializeItem(playInfoTextChannelUID, DEFAULT_TEST_ITEM_NAME, acceptedItemType);
736 waitForAssert(() -> {
737 verifyOnlineStatusIsSet();
741 private static Configuration createDefaultConfiguration() {
742 return createConfiguration(DEFAULT_CONFIG_PROPERTY_IP, DEFAULT_CONFIG_PROPERTY_PIN,
743 String.valueOf(DEFAULT_CONFIG_PROPERTY_PORT), DEFAULT_CONFIG_PROPERTY_REFRESH);
746 private static Configuration createConfiguration(String ip, String pin, String port, String refresh) {
747 Configuration config = new Configuration();
748 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_IP, ip);
749 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PIN, pin);
750 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PORT, new BigDecimal(port));
751 config.put(FSInternetRadioBindingConstants.CONFIG_PROPERTY_REFRESH, new BigDecimal(refresh));
755 private static void setTheChannelsMap() {
756 acceptedItemTypes = new HashMap<>();
757 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_POWER, "Switch");
758 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_MODE, "Number");
759 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_MUTE, "Switch");
760 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_NAME, "String");
761 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PLAY_INFO_TEXT, "String");
762 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_PRESET, "Number");
763 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_VOLUME_ABSOLUTE, "Number");
764 acceptedItemTypes.put(FSInternetRadioBindingConstants.CHANNEL_VOLUME_PERCENT, "Dimmer");
767 private void createThePowerChannel() {
768 String powerChannelID = FSInternetRadioBindingConstants.CHANNEL_POWER;
769 String acceptedItemType = acceptedItemTypes.get(powerChannelID);
770 powerChannel = createChannel(DEFAULT_THING_UID, powerChannelID, acceptedItemType);
773 private Item initializeItem(ChannelUID channelUID, String itemName, String acceptedItemType) {
776 switch (acceptedItemType) {
778 item = new NumberItem(itemName);
782 item = new StringItem(itemName);
786 item = new SwitchItem(itemName);
790 item = new DimmerItem(itemName);
797 private Channel createChannel(ThingUID thingUID, String channelID, String acceptedItemType) {
798 ChannelUID channelUID = new ChannelUID(thingUID, channelID);
800 Channel radioChannel = ChannelBuilder.create(channelUID, acceptedItemType).build();
801 channels.add(radioChannel);
805 private void testRadioThingConsideringConfiguration(Thing thing) {
806 Configuration config = thing.getConfiguration();
807 if (isConfigurationComplete(config)) {
808 waitForAssert(() -> {
809 verifyOnlineStatusIsSet();
812 waitForAssert(() -> {
813 verifyConfigurationError();
818 private boolean isConfigurationComplete(Configuration config) {
819 String ip = (String) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_IP);
820 BigDecimal port = (BigDecimal) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PORT.toString());
821 String pin = (String) config.get(FSInternetRadioBindingConstants.CONFIG_PROPERTY_PIN.toString());
823 if (ip == null || port.compareTo(BigDecimal.ZERO) == 0 || StringUtils.isEmpty(pin)) {
829 @SuppressWarnings("null")
830 private Thing initializeRadioThing(Configuration config) {
831 radioThing = ThingBuilder.create(DEFAULT_THING_TYPE_UID, DEFAULT_THING_UID).withConfiguration(config)
832 .withChannels(channels).build();
834 callback = mock(ThingHandlerCallback.class);
836 radioHandler = new FSInternetRadioHandler(radioThing, httpClient);
837 radioHandler.setCallback(callback);
838 radioThing.setHandler(radioHandler);
839 radioThing.getHandler().initialize();
844 @SuppressWarnings("null")
845 private Thing initializeRadioThingWithMockedHandler(Configuration config) {
846 radioThing = ThingBuilder.create(DEFAULT_THING_TYPE_UID, DEFAULT_THING_UID).withConfiguration(config)
847 .withChannels(channels).build();
849 callback = mock(ThingHandlerCallback.class);
851 radioHandler = new MockedRadioHandler(radioThing, httpClient);
852 radioHandler.setCallback(callback);
853 radioThing.setHandler(radioHandler);
854 radioThing.getHandler().initialize();
859 private void turnTheRadioOn(Thing radioThing) {
860 radioHandler.handleCommand(getChannelUID(radioThing, FSInternetRadioBindingConstants.CHANNEL_POWER),
863 final FrontierSiliconRadio radio = HandlerUtils.getRadio(radioHandler);
865 waitForAssert(() -> {
867 assertTrue(radio.getPower());
868 } catch (IOException ex) {
869 throw new AssertionError("I/O error", ex);
874 private void verifyOnlineStatusIsSet() {
875 ThingStatusInfoBuilder statusBuilder = ThingStatusInfoBuilder.create(ThingStatus.ONLINE,
876 ThingStatusDetail.NONE);
877 ThingStatusInfo statusInfo = statusBuilder.withDescription(null).build();
878 verify(callback, atLeast(1)).statusUpdated(radioThing, statusInfo);
881 private void verifyConfigurationError() {
882 ThingStatusInfoBuilder statusBuilder = ThingStatusInfoBuilder.create(ThingStatus.OFFLINE,
883 ThingStatusDetail.CONFIGURATION_ERROR);
884 ThingStatusInfo statusInfo = statusBuilder.withDescription("Configuration incomplete").build();
885 verify(callback, atLeast(1)).statusUpdated(radioThing, statusInfo);
888 private void verifyCommunicationError(String exceptionMessage) {
889 ArgumentCaptor<ThingStatusInfo> captor = ArgumentCaptor.forClass(ThingStatusInfo.class);
890 verify(callback, atLeast(1)).statusUpdated(isA(Thing.class), captor.capture());
891 ThingStatusInfo status = captor.getValue();
892 assertThat(status.getStatus(), is(ThingStatus.OFFLINE));
893 assertThat(status.getStatusDetail(), is(ThingStatusDetail.COMMUNICATION_ERROR));
894 assertThat(status.getDescription().contains(exceptionMessage), is(true));