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.bosesoundtouch.internal;
15 import static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.mockito.ArgumentMatchers.eq;
17 import static org.mockito.ArgumentMatchers.notNull;
19 import java.text.MessageFormat;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.junit.jupiter.api.BeforeEach;
24 import org.junit.jupiter.api.Disabled;
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.api.extension.ExtendWith;
27 import org.mockito.Mock;
28 import org.mockito.Mockito;
29 import org.mockito.junit.jupiter.MockitoExtension;
30 import org.openhab.binding.bosesoundtouch.internal.handler.BoseSoundTouchHandler;
31 import org.openhab.binding.bosesoundtouch.internal.handler.InMemmoryContentStorage;
32 import org.openhab.core.config.core.Configuration;
33 import org.openhab.core.library.types.PercentType;
34 import org.openhab.core.library.types.StringListType;
35 import org.openhab.core.storage.Storage;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.thing.ThingUID;
39 import org.openhab.core.thing.binding.ThingHandlerCallback;
40 import org.openhab.core.thing.binding.builder.ChannelBuilder;
41 import org.openhab.core.thing.binding.builder.ThingBuilder;
45 * @author Leo Siepel - Initial contribution
48 @ExtendWith(MockitoExtension.class)
50 public class SoundTouch20Tests {
52 private @Mock @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback;
53 private @NonNullByDefault({}) Thing soundTouchThing;
54 private @NonNullByDefault({}) BoseSoundTouchHandler thingHandler;
55 private @NonNullByDefault({}) XMLResponseProcessor processor;
56 private ThingUID thingUID = new ThingUID(BoseSoundTouchBindingConstants.BINDING_ID, "soundtouch20");
57 private ChannelUID volumeChannelUID = new ChannelUID(thingUID, BoseSoundTouchBindingConstants.CHANNEL_VOLUME);
58 private ChannelUID presetChannelUID = new ChannelUID(thingUID, BoseSoundTouchBindingConstants.CHANNEL_PRESET);
59 private Storage<@NonNull ContentItem> storage = new InMemmoryContentStorage();
60 private @Mock @NonNullByDefault({}) BoseStateDescriptionOptionProvider stateDescriptionProvider;
63 public void initialize() {
65 Configuration config = new Configuration();
66 config.put(BoseSoundTouchConfiguration.MAC_ADDRESS, "B0D5CC1AAAA1");
68 soundTouchThing = ThingBuilder.create(BoseSoundTouchBindingConstants.BST_20_THING_TYPE_UID, thingUID)
69 .withConfiguration(config).withChannel(ChannelBuilder.create(volumeChannelUID, "Number").build())
70 .withChannel(ChannelBuilder.create(presetChannelUID, "Number").build()).build();
72 PresetContainer container = new PresetContainer(storage);
73 thingHandler = new BoseSoundTouchHandler(soundTouchThing, container, stateDescriptionProvider);
74 processor = new XMLResponseProcessor(thingHandler);
77 private void processIncomingMessage(String mesage) {
79 processor.handleMessage(mesage);
80 } catch (Exception e) {
81 assert false : MessageFormat.format("handleMessage throws an exception: {0} Stacktrace: {1}",
82 e.getMessage(), e.getStackTrace());
87 public void configurationPropertyUpdated() {
89 CommandExecutor executor = new CommandExecutor(thingHandler);
90 thingHandler.setCommandExecutor(executor);
91 String message = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><msg><header deviceID=\"B0D5CC1AAAA1\" url=\"info\" method=\"GET\"><request requestID=\"0\" msgType=\"RESPONSE\"><info type=\"new\" /></request></header><body><info deviceID=\"B0D5CC1AAAA1\"><name>livingroom</name><type>SoundTouch 20</type><margeAccountUUID>3504027</margeAccountUUID><components><component><componentCategory>SCM</componentCategory><softwareVersion>27.0.6.46330.5043500 epdbuild.trunk.hepdswbld04.2022-08-04T11:20:29</softwareVersion><serialNumber>U6148010803720048000100</serialNumber></component><component><componentCategory>PackagedProduct</componentCategory><serialNumber>069430P5227013812</serialNumber></component></components><margeURL>https://streaming.bose.com</margeURL><networkInfo type=\"SCM\"><macAddress>B0D5CC1AAAA1</macAddress><ipAddress>192.168.1.1</ipAddress></networkInfo><networkInfo type=\"SMSC\"><macAddress>5CF821E2FD76</macAddress><ipAddress>192.168.1.1</ipAddress></networkInfo><moduleType>sm2</moduleType><variant>spotty</variant><variantMode>normal</variantMode><countryCode>GB</countryCode><regionCode>GB</regionCode></info></body></msg>";
94 processIncomingMessage(message);
97 assertEquals("27.0.6.46330.5043500",
98 soundTouchThing.getProperties().get(org.openhab.core.thing.Thing.PROPERTY_FIRMWARE_VERSION));
102 public void channelVolumeUpdated() {
104 CommandExecutor executor = new CommandExecutor(thingHandler);
106 thingHandler.setCommandExecutor(executor);
107 Mockito.when(thingHandlerCallback.isChannelLinked((ChannelUID) notNull())).thenReturn(true);
108 thingHandler.setCallback(thingHandlerCallback);
109 String message = "<updates deviceID=\"B0D5CC1AAAA1\"><volumeUpdated><volume><actualvolume>27</actualvolume></volume></volumeUpdated></updates>";
112 processIncomingMessage(message);
115 Mockito.verify(thingHandlerCallback).stateUpdated(eq(volumeChannelUID), eq(new PercentType("27")));
120 public void channelPresetUpdated() {
122 CommandExecutor executor = new CommandExecutor(thingHandler);
124 thingHandler.setCommandExecutor(executor);
125 Mockito.when(thingHandlerCallback.isChannelLinked((ChannelUID) notNull())).thenReturn(true);
126 thingHandler.setCallback(thingHandlerCallback);
127 String message = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><presets><preset id=\"1\" createdOn=\"1502124154\" updatedOn=\"1644607971\"><ContentItem source=\"TUNEIN\" type=\"stationurl\" location=\"/v1/playback/station/s25077\" sourceAccount=\"\" isPresetable=\"true\"><itemName>Radio FM1</itemName><containerArt>http://cdn-profiles.tunein.com/s25077/images/logoq.jpg</containerArt></ContentItem></preset><preset id=\"2\" createdOn=\"1485893875\" updatedOn=\"1612895566\"><ContentItem source=\"STORED_MUSIC\" location=\"22$2955\" sourceAccount=\"00113254-f4eb-0011-ebf4-ebf454321100/0\" isPresetable=\"true\"><itemName>Medicine At Midnight</itemName><containerArt /></ContentItem></preset><preset id=\"3\" createdOn=\"1506167722\" updatedOn=\"1506167722\"><ContentItem source=\"STORED_MUSIC\" location=\"22$1421\" sourceAccount=\"00113254-f4eb-0011-ebf4-ebf454321100/0\" isPresetable=\"true\"><itemName>Concrete & Gold</itemName><containerArt /></ContentItem></preset><preset id=\"4\" createdOn=\"1444146657\" updatedOn=\"1542740566\"><ContentItem source=\"TUNEIN\" type=\"stationurl\" location=\"/v1/playback/station/s24896\" sourceAccount=\"\" isPresetable=\"true\"><itemName>SWR3</itemName><containerArt>http://radiotime-logos.s3.amazonaws.com/s24896q.png</containerArt></ContentItem></preset><preset id=\"5\" createdOn=\"1468517184\" updatedOn=\"1542740566\"><ContentItem source=\"TUNEIN\" type=\"stationurl\" location=\"/v1/playback/station/s103302\" sourceAccount=\"\" isPresetable=\"true\"><itemName>SRF 3</itemName><containerArt>http://radiotime-logos.s3.amazonaws.com/s24862q.png</containerArt></ContentItem></preset><preset id=\"6\" createdOn=\"1481548081\" updatedOn=\"1524211387\"><ContentItem source=\"STORED_MUSIC\" location=\"22$882\" sourceAccount=\"00113254-f4eb-0011-ebf4-ebf454321100/0\" isPresetable=\"true\"><itemName>Sonic Highways</itemName><containerArt /></ContentItem></preset></presets>";
130 processIncomingMessage(message);
133 // TODO: check if preset channels have changed
134 Mockito.verify(thingHandlerCallback).stateUpdated(eq(presetChannelUID), eq(new StringListType("27")));