Refactor ThingManagerImpl (#3330)

* Refactor ThingManagerImpl

This

- moves config description URI to type-base class
- decouples the thing manager from bundle loading
- makes sure that all thing/channel-types and config descriptions are available when the thing is initialized
- enables thing type updates

Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
J-N-K
2023-02-15 22:03:59 +01:00
committed by GitHub
parent 7f5694856d
commit d7fa5534bf
29 changed files with 2070 additions and 765 deletions
@@ -166,7 +166,7 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// check getBridge works
assertThat(getBridge().getUID().toString(), is("bindingId:type1:bridgeId"));
assertThat(getBridge().getUID().toString(), is(BINDING_ID + ":type1:bridgeId"));
}
@Override
@@ -206,12 +206,17 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
ThingTypeUID bridgeTypeUID = new ThingTypeUID("bindingId:type1");
ThingUID bridgeUID = new ThingUID("bindingId:type1:bridgeId");
ThingTypeUID bridgeTypeUID = new ThingTypeUID(BINDING_ID, "type1");
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, "type2");
ThingType bridgeType = ThingTypeBuilder.instance(bridgeTypeUID, "bridge").buildBridge();
ThingType thingType = ThingTypeBuilder.instance(thingTypeUID, "thing").build();
registerThingTypeProvider(bridgeType, thingType);
ThingUID bridgeUID = new ThingUID(BINDING_ID, "type1", "bridgeId");
Bridge bridge = BridgeBuilder.create(bridgeTypeUID, bridgeUID).build();
ThingTypeUID thingTypeUID = new ThingTypeUID("bindingId:type2");
ThingUID thingUID = new ThingUID("bindingId:type2:thingId");
ThingUID thingUID = new ThingUID(BINDING_ID, "type2", "thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID).withBridge(bridge.getUID()).build();
managedThingProvider.add(bridge);
@@ -234,7 +239,7 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
});
// the assertion is in handle command
handler.handleCommand(new ChannelUID("bindingId:type2:thingId:channel"), RefreshType.REFRESH);
handler.handleCommand(new ChannelUID(thingUID, "thingId", "channel"), RefreshType.REFRESH);
unregisterService(ThingHandlerFactory.class.getName());
thingHandlerFactory.deactivate(componentContextMock);
@@ -245,10 +250,12 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
ConfigStatusProviderThingHandlerFactory thingHandlerFactory = new ConfigStatusProviderThingHandlerFactory();
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
registerDefaultThingTypeAndConfigDescription();
ThingTypeUID thingTypeUID = new ThingTypeUID("bindingId:type");
ThingUID thingUID = new ThingUID("bindingId:type:thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID).build();
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
ThingUID thingUID = new ThingUID(thingTypeUID, "thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID)
.withConfiguration(new Configuration(Map.of("parameter", ""))).build();
managedThingProvider.add(thing);
@@ -301,10 +308,12 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
ConfigStatusProviderThingHandlerFactory thingHandlerFactory = new ConfigStatusProviderThingHandlerFactory();
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
registerDefaultThingTypeAndConfigDescription();
ThingTypeUID thingTypeUID = new ThingTypeUID("bindingId:type");
ThingUID thingUID = new ThingUID("bindingId:type:thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID).build();
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
ThingUID thingUID = new ThingUID(thingTypeUID, "thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID)
.withConfiguration(new Configuration(Map.of("parameter", "ok"))).build();
managedThingProvider.add(thing);
@@ -329,30 +338,30 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
Thread.sleep(2000);
thing.getHandler().handleConfigurationUpdate(Map.of("param", "invalid"));
thing.getHandler().handleConfigurationUpdate(Map.of("parameter", "invalid"));
waitForAssert(() -> {
Event event = eventSubscriber.getReceivedEvent();
assertThat(event, is(notNullValue()));
assertThat(event.getPayload(), CoreMatchers
.containsString("\"parameterName\":\"param\",\"type\":\"ERROR\",\"message\":\"param invalid\"}"));
assertThat(event.getPayload(), CoreMatchers.containsString(
"\"parameterName\":\"parameter\",\"type\":\"ERROR\",\"message\":\"param invalid\"}"));
eventSubscriber.resetReceivedEvent();
}, 2500, DFL_SLEEP_TIME);
thing.getHandler().handleConfigurationUpdate(Map.of("param", "ok"));
thing.getHandler().handleConfigurationUpdate(Map.of("parameter", "ok"));
waitForAssert(() -> {
Event event = eventSubscriber.getReceivedEvent();
assertThat(event, is(notNullValue()));
assertThat(event.getPayload(), CoreMatchers
.containsString("\"parameterName\":\"param\",\"type\":\"INFORMATION\",\"message\":\"param ok\"}"));
assertThat(event.getPayload(), CoreMatchers.containsString(
"\"parameterName\":\"parameter\",\"type\":\"INFORMATION\",\"message\":\"param ok\"}"));
}, 2500, DFL_SLEEP_TIME);
}
@Test
public void assertBaseThingHandlerNotifiesThingManagerAboutConfigurationUpdates() {
// register ThingTypeProvider & ConfigurationDescription with 'required' parameter
registerThingTypeProvider();
registerDefaultThingTypeProvider();
registerConfigDescriptionProvider(true);
// register thing handler factory
@@ -396,7 +405,7 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
static class ConfigStatusProviderThingHandler extends ConfigStatusThingHandler {
private static final String PARAM = "param";
private static final String PARAM = "parameter";
private static final ConfigStatusMessage ERROR = ConfigStatusMessage.Builder.error(PARAM)
.withMessageKeySuffix("param.invalid").build();
private static final ConfigStatusMessage INFO = ConfigStatusMessage.Builder.information(PARAM)
@@ -448,7 +457,7 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
public void initialize() {
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannel(
ChannelBuilder.create(new ChannelUID("bindingId:type:thingId:1"), CoreItemFactory.STRING).build());
ChannelBuilder.create(new ChannelUID(thing.getUID(), "1"), CoreItemFactory.STRING).build());
updateThing(thingBuilder.build());
updateStatus(ThingStatus.ONLINE);
}
@@ -493,18 +502,21 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
@Test
public void assertThingCanBeUpdatedFromThingHandler() {
registerThingTypeProvider();
registerDefaultThingTypeProvider();
YetAnotherThingHandlerFactory thingHandlerFactory = new YetAnotherThingHandlerFactory();
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
final ThingRegistryChangeListener listener = new ThingRegistryChangeListener();
registerDefaultThingTypeAndConfigDescription();
try {
thingRegistry.addRegistryChangeListener(listener);
ThingTypeUID thingTypeUID = new ThingTypeUID("bindingId:type");
ThingUID thingUID = new ThingUID("bindingId:type:thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID).build();
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
ThingUID thingUID = new ThingUID(thingTypeUID, "thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID)
.withConfiguration(new Configuration(Map.of("parameter", ""))).build();
assertThat(thing.getChannels().size(), is(0));
managedThingProvider.add(thing);
@@ -522,17 +534,20 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
@Test
public void assertPropertiesCanBeUpdatedFromThingHandler() {
registerThingTypeProvider();
registerDefaultThingTypeProvider();
YetAnotherThingHandlerFactory thingHandlerFactory = new YetAnotherThingHandlerFactory();
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
registerDefaultThingTypeAndConfigDescription();
final ThingRegistryChangeListener listener = new ThingRegistryChangeListener();
try {
thingRegistry.addRegistryChangeListener(listener);
Thing thing = ThingBuilder
.create(new ThingTypeUID("bindingId:type"), new ThingUID("bindingId:type:thingId")).build();
.create(new ThingTypeUID(BINDING_ID, THING_TYPE_ID),
new ThingUID(BINDING_ID, THING_TYPE_ID, "thingId"))
.withConfiguration(new Configuration(Map.of("parameter", ""))).build();
managedThingProvider.add(thing);
@@ -577,10 +592,12 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
final ThingRegistryChangeListener listener = new ThingRegistryChangeListener();
registerDefaultThingTypeAndConfigDescription();
try {
thingRegistry.addRegistryChangeListener(listener);
ThingTypeUID thingTypeUID = new ThingTypeUID("bindingId:type");
ThingUID thingUID = new ThingUID("bindingId:type:thingId");
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
ThingUID thingUID = new ThingUID(thingTypeUID, "thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID).build();
managedThingProvider.add(thing);
@@ -601,11 +618,11 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
registerThingTypeProvider();
registerConfigDescriptionProvider(true);
registerDefaultThingTypeProvider();
ThingTypeUID thingTypeUID = new ThingTypeUID("bindingId:type");
ThingUID thingUID = new ThingUID("bindingId:type:thingId");
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
ThingUID thingUID = new ThingUID(thingTypeUID, "thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID)
.withConfiguration(new Configuration(Map.of("parameter", "someValue"))).build();
@@ -624,11 +641,11 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
registerThingTypeProvider();
registerDefaultThingTypeProvider();
registerConfigDescriptionProvider(true);
ThingTypeUID thingTypeUID = new ThingTypeUID("bindingId:type");
ThingUID thingUID = new ThingUID("bindingId:type:thingId");
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
ThingUID thingUID = new ThingUID(thingTypeUID, "thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID)
.withConfiguration(new Configuration(Map.of("parameter", "someValue"))).build();
@@ -658,12 +675,12 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
registerThingTypeAndConfigDescription();
registerDefaultThingTypeAndConfigDescription();
ThingRegistry thingRegistry = getService(ThingRegistry.class);
ThingTypeUID thingTypeUID = new ThingTypeUID("bindingId:type");
ThingUID thingUID = new ThingUID("bindingId:type:thingId");
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
ThingUID thingUID = new ThingUID(thingTypeUID, "thingId");
Thing thing = ThingBuilder.create(thingTypeUID, thingUID).build();
managedThingProvider.add(thing);
@@ -694,13 +711,17 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
thingHandlerFactory.activate(componentContextMock);
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
ThingTypeUID thingType1 = new ThingTypeUID("bindingId:type1");
ThingTypeUID thingType2 = new ThingTypeUID("bindingId:type2");
ThingTypeUID thingTypeUID1 = new ThingTypeUID(BINDING_ID, "type1");
ThingTypeUID thingTypeUID2 = new ThingTypeUID(BINDING_ID, "type2");
Bridge bridge = BridgeBuilder.create(thingType1, new ThingUID("bindingId:type1:bridgeId")).build();
Thing thingA = ThingBuilder.create(thingType2, new ThingUID("bindingId:type2:thingIdA"))
ThingType thingType1 = ThingTypeBuilder.instance(thingTypeUID1, thingTypeUID1.getId()).build();
ThingType thingType2 = ThingTypeBuilder.instance(thingTypeUID2, thingTypeUID2.getId()).build();
registerThingTypeProvider(thingType1, thingType2);
Bridge bridge = BridgeBuilder.create(thingTypeUID1, new ThingUID(thingTypeUID1, "bridgeId")).build();
Thing thingA = ThingBuilder.create(thingTypeUID2, new ThingUID(thingTypeUID2, "thingIdA"))
.withBridge(bridge.getUID()).build();
Thing thingB = ThingBuilder.create(thingType2, new ThingUID("bindingId:type2:thingIdB"))
Thing thingB = ThingBuilder.create(thingTypeUID2, new ThingUID(thingTypeUID2, "thingIdB"))
.withBridge(bridge.getUID()).build();
assertThat(bridge.getStatus(), is(ThingStatus.UNINITIALIZED));
@@ -751,40 +772,35 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
return null;
}
private void registerThingTypeAndConfigDescription() {
ThingType thingType = ThingTypeBuilder.instance(new ThingTypeUID(BINDING_ID, THING_TYPE_ID), "label")
.withConfigDescriptionURI(BINDING_CONFIG_URI).build();
private void registerDefaultThingTypeAndConfigDescription() {
registerDefaultThingTypeProvider();
ConfigDescription configDescription = ConfigDescriptionBuilder.create(BINDING_CONFIG_URI)
.withParameter(ConfigDescriptionParameterBuilder
.create("parameter", ConfigDescriptionParameter.Type.TEXT).withRequired(true).build())
.build();
ThingTypeProvider thingTypeProvider = mock(ThingTypeProvider.class);
when(thingTypeProvider.getThingType(ArgumentMatchers.any(ThingTypeUID.class),
ArgumentMatchers.any(Locale.class))).thenReturn(thingType);
registerService(thingTypeProvider);
ThingTypeRegistry thingTypeRegistry = mock(ThingTypeRegistry.class);
when(thingTypeRegistry.getThingType(ArgumentMatchers.any(ThingTypeUID.class))).thenReturn(thingType);
registerService(thingTypeRegistry);
ConfigDescriptionProvider configDescriptionProvider = mock(ConfigDescriptionProvider.class);
when(configDescriptionProvider.getConfigDescription(ArgumentMatchers.any(URI.class),
ArgumentMatchers.nullable(Locale.class))).thenReturn(configDescription);
when(configDescriptionProvider.getConfigDescription(eq(BINDING_CONFIG_URI), nullable(Locale.class)))
.thenReturn(configDescription);
registerService(configDescriptionProvider);
}
private void registerThingTypeProvider() {
private void registerDefaultThingTypeProvider() {
ThingType thingType = ThingTypeBuilder.instance(new ThingTypeUID(BINDING_ID, THING_TYPE_ID), "label")
.withConfigDescriptionURI(BINDING_CONFIG_URI).build();
registerThingTypeProvider(thingType);
}
private void registerThingTypeProvider(ThingType... thingTypes) {
ThingTypeProvider thingTypeProvider = mock(ThingTypeProvider.class);
when(thingTypeProvider.getThingType(ArgumentMatchers.any(ThingTypeUID.class),
ArgumentMatchers.nullable(Locale.class))).thenReturn(thingType);
registerService(thingTypeProvider);
ThingTypeRegistry thingTypeRegistry = mock(ThingTypeRegistry.class);
when(thingTypeRegistry.getThingType(ArgumentMatchers.any(ThingTypeUID.class))).thenReturn(thingType);
for (ThingType thingType : thingTypes) {
when(thingTypeProvider.getThingType(eq(thingType.getUID()), nullable(Locale.class))).thenReturn(thingType);
when(thingTypeRegistry.getThingType(eq(thingType.getUID()))).thenReturn(thingType);
}
registerService(thingTypeProvider);
registerService(thingTypeRegistry);
}
@@ -796,8 +812,8 @@ public class BindingBaseClassesOSGiTest extends JavaOSGiTest {
.build();
ConfigDescriptionProvider configDescriptionProvider = mock(ConfigDescriptionProvider.class);
when(configDescriptionProvider.getConfigDescription(ArgumentMatchers.any(URI.class),
ArgumentMatchers.nullable(Locale.class))).thenReturn(configDescription);
when(configDescriptionProvider.getConfigDescription(ArgumentMatchers.any(URI.class), nullable(Locale.class)))
.thenReturn(configDescription);
registerService(configDescriptionProvider);
}
}
@@ -437,13 +437,17 @@ public class ChangeThingTypeOSGiTest extends JavaOSGiTest {
}
private List<ChannelDefinition> getChannelDefinitions(ThingTypeUID thingTypeUID) throws URISyntaxException {
URI configDescriptionUri = new URI("scheme", "channelType:" + thingTypeUID.getId(), null);
ChannelTypeUID channelTypeUID = new ChannelTypeUID("test:" + thingTypeUID.getId());
ChannelType channelType = ChannelTypeBuilder.state(channelTypeUID, "channelLabel", "itemType")
.withDescription("description") //
.withCategory("category") //
.withConfigDescriptionURI(new URI("scheme", "channelType:" + thingTypeUID.getId(), null)).build();
.withConfigDescriptionURI(configDescriptionUri).build();
channelTypes.put(channelTypeUID, channelType);
ConfigDescription configDescription = ConfigDescriptionBuilder.create(configDescriptionUri).build();
configDescriptions.put(configDescriptionUri, configDescription);
ChannelDefinition cd = new ChannelDefinitionBuilder("channel" + thingTypeUID.getId(), channelTypeUID).build();
return List.of(cd);
}
@@ -39,9 +39,13 @@ import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.binding.ThingTypeProvider;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
import org.openhab.core.thing.testutil.i18n.DefaultLocaleSetter;
import org.openhab.core.thing.type.ThingType;
import org.openhab.core.thing.type.ThingTypeBuilder;
import org.openhab.core.thing.type.ThingTypeRegistry;
import org.openhab.core.types.Command;
import org.openhab.core.util.BundleResolver;
import org.osgi.framework.Bundle;
@@ -88,6 +92,8 @@ public class ThingStatusInfoI18nLocalizationServiceOSGiTest extends JavaOSGiTest
simpleThingHandlerFactory.activate(componentContext);
registerService(simpleThingHandlerFactory, ThingHandlerFactory.class.getName());
registerThingType();
thing = ThingBuilder.create(new ThingTypeUID("aaa:bbb"), "ccc").build();
managedThingProvider = getService(ManagedThingProvider.class);
@@ -330,4 +336,17 @@ public class ThingStatusInfoI18nLocalizationServiceOSGiTest extends JavaOSGiTest
}
}
}
private void registerThingType() {
ThingType thingType = ThingTypeBuilder.instance(new ThingTypeUID("aaa:bbb"), "label").build();
ThingTypeProvider thingTypeProvider = mock(ThingTypeProvider.class);
ThingTypeRegistry thingTypeRegistry = mock(ThingTypeRegistry.class);
when(thingTypeProvider.getThingType(eq(thingType.getUID()), nullable(Locale.class))).thenReturn(thingType);
when(thingTypeRegistry.getThingType(eq(thingType.getUID()))).thenReturn(thingType);
registerService(thingTypeProvider);
registerService(thingTypeRegistry);
}
}
@@ -17,12 +17,15 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -38,6 +41,8 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.openhab.core.common.registry.RegistryChangeListener;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventSubscriber;
@@ -59,6 +64,7 @@ import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.binding.ThingTypeProvider;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.events.AbstractThingRegistryEvent;
@@ -71,7 +77,14 @@ import org.openhab.core.thing.link.dto.ItemChannelLinkDTO;
import org.openhab.core.thing.link.events.AbstractItemChannelLinkRegistryEvent;
import org.openhab.core.thing.link.events.ItemChannelLinkRemovedEvent;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeBuilder;
import org.openhab.core.thing.type.ChannelTypeProvider;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.thing.type.ThingType;
import org.openhab.core.thing.type.ThingTypeBuilder;
import org.openhab.core.thing.type.ThingTypeRegistry;
import org.openhab.core.thing.util.ThingHandlerHelper;
import org.openhab.core.types.Command;
import org.openhab.core.util.BundleResolver;
@@ -85,6 +98,7 @@ import org.slf4j.LoggerFactory;
* @author Wouter Born - Initial contribution
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@NonNullByDefault
public class ChannelLinkNotifierOSGiTest extends JavaOSGiTest {
@@ -162,6 +176,7 @@ public class ChannelLinkNotifierOSGiTest extends JavaOSGiTest {
@BeforeEach
public void beforeEach() {
registerVolatileStorageService();
registerThingAndChannelTypeProvider();
itemChannelLinkRegistry = getService(ItemChannelLinkRegistry.class);
assertThat(itemChannelLinkRegistry, is(notNullValue()));
@@ -184,14 +199,8 @@ public class ChannelLinkNotifierOSGiTest extends JavaOSGiTest {
when(thingHandlerFactoryMock.supportsThingType(eq(THING_TYPE_UID))).thenReturn(true);
registerService(thingHandlerFactoryMock);
when(bundleMock.getSymbolicName()).thenReturn("org.openhab.core.thing");
when(bundleResolverMock.resolveBundle(any())).thenReturn(bundleMock);
ThingManagerImpl thingManager = (ThingManagerImpl) getService(ThingManager.class);
assertThat(thingManager, is(notNullValue()));
if (thingManager != null) {
thingManager.setBundleResolver(bundleResolverMock);
}
}
@AfterEach
@@ -560,4 +569,27 @@ public class ChannelLinkNotifierOSGiTest extends JavaOSGiTest {
assertNoChannelLinkEventsReceived(subjectThing);
assertNoChannelLinkEventsReceived(otherThing);
}
private void registerThingAndChannelTypeProvider() {
ThingType thingType = ThingTypeBuilder.instance(THING_TYPE_UID, "label").build();
ThingTypeProvider thingTypeProvider = mock(ThingTypeProvider.class);
when(thingTypeProvider.getThingType(any(ThingTypeUID.class), nullable(Locale.class))).thenReturn(thingType);
registerService(thingTypeProvider);
ThingTypeRegistry thingTypeRegistry = mock(ThingTypeRegistry.class);
when(thingTypeRegistry.getThingType(any(ThingTypeUID.class))).thenReturn(thingType);
registerService(thingTypeRegistry);
ChannelType channelType = ChannelTypeBuilder.state(CHANNEL_TYPE_UID, "Number", "Number").build();
ChannelTypeProvider channelTypeProvider = mock(ChannelTypeProvider.class);
when(channelTypeProvider.getChannelType(any(ChannelTypeUID.class), nullable(Locale.class)))
.thenReturn(channelType);
registerService(channelTypeProvider);
ChannelTypeRegistry channelTypeRegistry = mock(ChannelTypeRegistry.class);
when(channelTypeRegistry.getChannelType(any(ChannelTypeUID.class))).thenReturn(channelType);
registerService(channelTypeRegistry);
}
}
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.*;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;
@@ -112,6 +113,8 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
CHANNEL_GROUP_ID);
private static final ThingTypeUID THING_TYPE_UID = new ThingTypeUID("binding:thing");
private static final ThingTypeUID THING_WITH_BRIDGE_TYPE_UID = new ThingTypeUID("binding:thingWithBridge");
private static final ThingUID THING_UID = new ThingUID(THING_TYPE_UID, "thing");
private static final ThingTypeUID BRIDGE_TYPE_UID = new ThingTypeUID("binding:bridge");
@@ -127,6 +130,7 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
private @NonNullByDefault({}) ThingRegistry thingRegistry;
private @NonNullByDefault({}) ReadyService readyService;
private @NonNullByDefault({}) Storage<Boolean> storage;
private @NonNullByDefault({}) ThingManager thingManager;
private @NonNullByDefault({}) URI configDescriptionChannel;
@@ -137,6 +141,12 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
public void setUp() throws Exception {
configDescriptionChannel = new URI("test:channel");
configDescriptionThing = new URI("test:test");
registerThingTypeProvider();
registerChannelTypeProvider();
registerChannelGroupTypeProvider();
registerConfigDescriptions();
thing = ThingBuilder.create(THING_TYPE_UID, THING_UID).withChannels(List.of( //
ChannelBuilder.create(CHANNEL_UID, CoreItemFactory.SWITCH).withLabel("Test Label")
.withDescription("Test Description").withType(CHANNEL_TYPE_UID)
@@ -187,8 +197,56 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
}
@Test
public void testInitializeCallsThingUpdated() throws Exception {
registerThingTypeProvider();
public void testMissingBridgePreventsInitialization() {
registerThingHandlerFactory(THING_WITH_BRIDGE_TYPE_UID, thing -> new BaseThingHandler(thing) {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void initialize() {
updateStatus(ThingStatus.ONLINE);
}
});
Thing thing = ThingBuilder.create(THING_WITH_BRIDGE_TYPE_UID, THING_UID).build();
managedThingProvider.add(thing);
waitForAssert(() -> {
assertEquals(ThingStatus.UNINITIALIZED, thing.getStatus());
assertEquals(ThingStatusDetail.HANDLER_CONFIGURATION_PENDING, thing.getStatusInfo().getStatusDetail(),
thing.getStatusInfo().toString());
});
managedThingProvider.remove(thing.getUID());
}
@Test
public void testExistingBridgeAllowsInitialization() {
registerThingHandlerFactory(THING_WITH_BRIDGE_TYPE_UID, thing -> new BaseThingHandler(thing) {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void initialize() {
updateStatus(ThingStatus.ONLINE);
}
});
Thing thing = ThingBuilder.create(THING_WITH_BRIDGE_TYPE_UID, THING_UID).withBridge(BRIDGE_UID).build();
managedThingProvider.add(thing);
waitForAssert(() -> {
assertEquals(ThingStatus.UNINITIALIZED, thing.getStatus());
assertEquals(ThingStatusDetail.BRIDGE_UNINITIALIZED, thing.getStatusInfo().getStatusDetail());
});
managedThingProvider.remove(thing.getUID());
}
@Test
public void testInitializeCallsThingUpdated() {
AtomicReference<ThingHandlerCallback> thc = new AtomicReference<>();
AtomicReference<Boolean> initializeRunning = new AtomicReference<>(false);
registerThingHandlerFactory(THING_TYPE_UID, thing -> {
@@ -325,9 +383,7 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
}
@Test
public void testInitializeOnlyIfInitializable() throws Exception {
registerThingTypeProvider();
registerChannelTypeProvider();
public void testInitializeOnlyIfInitializable() {
registerThingHandlerFactory(THING_TYPE_UID, thing -> new BaseThingHandler(thing) {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
@@ -563,9 +619,7 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
}
@Test
public void testSetEnabledWithHandler() throws Exception {
registerThingTypeProvider();
public void testSetEnabledWithHandler() {
AtomicReference<ThingHandlerCallback> thingHandlerCallback = new AtomicReference<>();
AtomicReference<Boolean> initializeInvoked = new AtomicReference<>(false);
AtomicReference<Boolean> disposeInvoked = new AtomicReference<>(false);
@@ -640,8 +694,6 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
@Test
public void testSetEnabledWithoutHandlerFactory() throws Exception {
registerThingTypeProvider();
ThingStatusInfo thingStatusInfo = ThingStatusInfoBuilder
.create(ThingStatus.UNINITIALIZED, ThingStatusDetail.NONE).build();
thing.setStatusInfo(thingStatusInfo);
@@ -732,9 +784,7 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
}
@Test
public void testDisposeNotInvokedOnAlreadyDisabledThing() throws Exception {
registerThingTypeProvider();
public void testDisposeNotInvokedOnAlreadyDisabledThing() {
AtomicReference<ThingHandlerCallback> thingHandlerCallback = new AtomicReference<>();
AtomicReference<Boolean> initializeInvoked = new AtomicReference<>(false);
AtomicReference<Boolean> disposeInvoked = new AtomicReference<>(false);
@@ -809,9 +859,7 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
}
@Test
public void testUpdateThing() throws Exception {
registerThingTypeProvider();
public void testUpdateThing() {
AtomicReference<ThingHandlerCallback> thingHandlerCallback = new AtomicReference<>();
AtomicReference<Boolean> initializeInvoked = new AtomicReference<>(false);
AtomicReference<Boolean> disposeInvoked = new AtomicReference<>(false);
@@ -897,9 +945,7 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
}
@Test
public void testStorageEntryRetainedOnThingRemoval() throws Exception {
registerThingTypeProvider();
public void testStorageEntryRetainedOnThingRemoval() {
AtomicReference<ThingHandlerCallback> thingHandlerCallback = new AtomicReference<>();
AtomicReference<Boolean> initializeInvoked = new AtomicReference<>(false);
AtomicReference<Boolean> disposeInvoked = new AtomicReference<>(false);
@@ -1002,28 +1048,37 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
registerService(mockThingHandlerFactory, ThingHandlerFactory.class.getName());
}
private void registerThingTypeProvider() throws Exception {
private void registerThingTypeProvider() {
ThingType thingType = ThingTypeBuilder.instance(THING_TYPE_UID, "label")
.withConfigDescriptionURI(configDescriptionThing)
.withChannelDefinitions(List.of(new ChannelDefinitionBuilder(CHANNEL_ID, CHANNEL_TYPE_UID).build()))
.build();
ThingType thingTypeWithBridge = ThingTypeBuilder.instance(THING_WITH_BRIDGE_TYPE_UID, "label")
.withConfigDescriptionURI(configDescriptionThing)
.withSupportedBridgeTypeUIDs(List.of(BRIDGE_TYPE_UID.getId()))
.withChannelDefinitions(List.of(new ChannelDefinitionBuilder(CHANNEL_ID, CHANNEL_TYPE_UID).build()))
.build();
ThingType bridgeType = ThingTypeBuilder.instance(BRIDGE_TYPE_UID, "label").buildBridge();
ThingTypeProvider mockThingTypeProvider = mock(ThingTypeProvider.class);
when(mockThingTypeProvider.getThingType(eq(THING_TYPE_UID), any())).thenReturn(thingType);
when(mockThingTypeProvider.getThingType(eq(THING_WITH_BRIDGE_TYPE_UID), any())).thenReturn(thingTypeWithBridge);
when(mockThingTypeProvider.getThingType(eq(BRIDGE_TYPE_UID), any())).thenReturn(bridgeType);
registerService(mockThingTypeProvider);
}
private void registerChannelTypeProvider() throws Exception {
private void registerChannelTypeProvider() {
ChannelType channelType = ChannelTypeBuilder.state(CHANNEL_TYPE_UID, "Test Label", CoreItemFactory.SWITCH)
.withDescription("Test Description").withCategory("Test Category").withTag("Test Tag")
.withConfigDescriptionURI(new URI("test:channel")).build();
.withConfigDescriptionURI(URI.create("test:channel")).build();
ChannelTypeProvider mockChannelTypeProvider = mock(ChannelTypeProvider.class);
when(mockChannelTypeProvider.getChannelType(eq(CHANNEL_TYPE_UID), any())).thenReturn(channelType);
registerService(mockChannelTypeProvider);
}
private void registerChannelGroupTypeProvider() throws Exception {
private void registerChannelGroupTypeProvider() {
ChannelGroupType channelGroupType = ChannelGroupTypeBuilder.instance(CHANNEL_GROUP_TYPE_UID, "Test Group Label")
.withDescription("Test Group Description").withCategory("Test Group Category")
.withChannelDefinitions(List.of(new ChannelDefinitionBuilder(CHANNEL_ID, CHANNEL_TYPE_UID).build(),
@@ -1064,9 +1119,6 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
};
private AtomicReference<ThingHandlerCallback> initializeThingHandlerCallback() throws Exception {
registerThingTypeProvider();
registerChannelTypeProvider();
registerChannelGroupTypeProvider();
AtomicReference<ThingHandlerCallback> thc = new AtomicReference<>();
ThingHandlerFactory thingHandlerFactory = new BaseThingHandlerFactory() {
@Override
@@ -1093,4 +1145,15 @@ public class ThingManagerOSGiJavaTest extends JavaOSGiTest {
});
return thc;
}
private void registerConfigDescriptions() {
ConfigDescriptionProvider configDescriptionProvider = mock(ConfigDescriptionProvider.class);
when(configDescriptionProvider.getConfigDescription(eq(configDescriptionThing), nullable(Locale.class)))
.thenReturn(ConfigDescriptionBuilder.create(configDescriptionThing).build());
when(configDescriptionProvider.getConfigDescription(eq(configDescriptionChannel), nullable(Locale.class)))
.thenReturn(ConfigDescriptionBuilder.create(configDescriptionChannel).build());
registerService(configDescriptionProvider);
}
}
@@ -58,8 +58,8 @@ import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.service.ReadyMarker;
import org.openhab.core.service.ReadyMarkerUtils;
import org.openhab.core.service.ReadyService;
import org.openhab.core.service.StartLevelService;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
@@ -100,9 +100,6 @@ import org.openhab.core.thing.type.ThingTypeBuilder;
import org.openhab.core.thing.type.ThingTypeRegistry;
import org.openhab.core.types.Command;
import org.openhab.core.util.BundleResolver;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
@@ -169,27 +166,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
readyService = getService(ReadyService.class);
assertNotNull(readyService);
waitForAssert(() -> {
try {
assertThat(
bundleContext
.getServiceReferences(ReadyMarker.class,
"(" + ThingManagerImpl.XML_THING_TYPE + "="
+ bundleContext.getBundle().getSymbolicName() + ")"),
is(notNullValue()));
} catch (InvalidSyntaxException e) {
fail("Failed to get service reference: " + e.getMessage());
}
});
Bundle bundle = mock(Bundle.class);
when(bundle.getSymbolicName()).thenReturn("org.openhab.core.thing");
BundleResolver bundleResolver = mock(BundleResolver.class);
when(bundleResolver.resolveBundle(any())).thenReturn(bundle);
ThingManagerImpl thingManager = (ThingManagerImpl) getService(ThingTypeMigrationService.class);
thingManager.setBundleResolver(bundleResolver);
assertNotNull(thingManager);
}
@AfterEach
@@ -199,6 +177,33 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
when(componentContext.getProperties()).thenReturn(new Hashtable<>());
}
@Test
public void thingManagerHonorsMissingPrerequisite() {
// set ready marker, otherwise check job is not running
ThingManagerImpl thingManager = (ThingManagerImpl) getService(ThingManager.class);
thingManager.onReadyMarkerAdded(new ReadyMarker(StartLevelService.STARTLEVEL_MARKER_TYPE,
Integer.toString(StartLevelService.STARTLEVEL_MODEL)));
ThingHandler thingHandler = mock(ThingHandler.class);
when(thingHandler.getThing()).thenReturn(thing);
ThingHandlerFactory thingHandlerFactory = mock(ThingHandlerFactory.class);
when(thingHandlerFactory.supportsThingType(any(ThingTypeUID.class))).thenReturn(true);
when(thingHandlerFactory.registerHandler(any(Thing.class))).thenReturn(thingHandler);
registerService(thingHandlerFactory);
managedThingProvider.add(thing);
assertThat(thing.getStatus(), is(ThingStatus.UNINITIALIZED));
assertThat(thing.getStatusInfo().getStatusDetail(), is(ThingStatusDetail.NOT_YET_READY));
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
waitForAssert(() -> assertThat(thing.getStatus(), is(ThingStatus.INITIALIZING)));
}
@Test
@SuppressWarnings("null")
public void thingManagerChangesTheThingType() {
@@ -231,6 +236,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@Test
public void thingManagerChangesTheThingTypeCorrectlyEvenIfInitializeTakesLongAndCalledFromThere() {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
ThingTypeUID newThingTypeUID = new ThingTypeUID("binding:type2");
@@ -317,6 +323,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@Test
public void thingManagerWaitsWithThingUpdatedUntilInitializeReturned() {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
Thing thing2 = ThingBuilder.create(THING_TYPE_UID, THING_UID)
.withChannels(List.of(ChannelBuilder.create(CHANNEL_UID, CoreItemFactory.SWITCH).build())).build();
@@ -379,6 +386,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
when(thingHandlerFactory.registerHandler(any(Thing.class))).thenReturn(thingHandler);
registerService(thingHandlerFactory);
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
managedThingProvider.add(thing);
@@ -395,6 +404,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
when(thingHandlerFactory.registerHandler(any(Thing.class))).thenReturn(thingHandler);
registerService(thingHandlerFactory);
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
managedThingProvider.add(thing);
managedThingProvider.remove(thing.getUID());
@@ -442,6 +453,9 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
.create(ThingStatus.UNINITIALIZED, ThingStatusDetail.NONE).build();
assertThat(thing.getStatusInfo(), is(uninitializedNone));
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
// add thing - provokes handler registration & initialization
managedThingProvider.add(thing);
waitForAssert(() -> verify(thingHandlerFactory, times(1)).registerHandler(thing));
@@ -521,6 +535,9 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
.create(ThingStatus.UNINITIALIZED, ThingStatusDetail.NONE).build();
assertThat(testThing.getStatusInfo(), is(uninitializedNone));
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
managedThingProvider.add(testThing);
final ThingStatusInfo uninitializedError = ThingStatusInfoBuilder
.create(ThingStatus.UNINITIALIZED, ThingStatusDetail.HANDLER_INITIALIZING_ERROR)
@@ -684,6 +701,9 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
ThingRegistry thingRegistry = getService(ThingRegistry.class);
assertThat(thingRegistry, not(nullValue()));
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
// add thing - no thing initialization, because bridge is not available
thingRegistry.add(thing);
waitForAssert(() -> assertThat(thingState.initCalled, is(false)));
@@ -767,6 +787,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@Test
public void thingManagerDoesNotDelegateUpdateEventsToItsSource() {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
class ThingHandlerState {
boolean handleCommandWasCalled;
@@ -829,6 +850,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@Test
public void thingManagerHandlesStateUpdatesCorrectly() {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
class ThingHandlerState {
boolean thingUpdatedWasCalled;
@@ -931,6 +953,9 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
ThingHandlerCallback callback;
}
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
final ThingHandlerState state = new ThingHandlerState();
ThingHandler thingHandler = mock(ThingHandler.class);
@@ -1000,6 +1025,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
final ThingHandlerState state = new ThingHandlerState();
ThingHandler thingHandler = mock(ThingHandler.class);
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
managedThingProvider.add(thing);
@@ -1048,6 +1075,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@Test
public void thingManagerHandlesThingStatusUpdatesUninitializedAndInitializingCorrectly() {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
ThingHandler thingHandler = mock(ThingHandler.class);
when(thingHandler.getThing()).thenReturn(thing);
@@ -1078,6 +1106,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@Test
public void thingManagerHandlesThingStatusUpdatesRemovingAndInitializingCorrectly() {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
ThingHandler thingHandler = mock(ThingHandler.class);
when(thingHandler.getThing()).thenReturn(thing);
@@ -1111,6 +1140,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
when(thingHandlerFactory.registerHandler(any(Thing.class))).thenThrow(new RuntimeException(exceptionMessage));
registerService(thingHandlerFactory);
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
managedThingProvider.add(thing);
@@ -1124,6 +1155,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@SuppressWarnings({ "null", "unchecked" })
public void thingManagerHandlesThingUpdatesCorrectly() {
String itemName = "name";
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
managedThingProvider.add(thing);
managedItemChannelLinkProvider.add(new ItemChannelLink(itemName, CHANNEL_UID));
@@ -1196,6 +1229,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@Test
public void thingManagerPostsThingStatusEventsIfTheStatusOfAThingIsUpdated() {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
class ThingHandlerState {
@Nullable
@@ -1283,6 +1317,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@Test
public void thingManagerPostsThingStatusChangedEventsIfTheStatusOfAThingIsChanged() throws Exception {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
class ThingHandlerState {
@Nullable
@@ -1355,6 +1390,7 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
@SuppressWarnings("null")
public void thingManagerPostsLocalizedThingStatusInfoAndThingStatusInfoChangedEvents() throws Exception {
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
class ThingHandlerState {
@Nullable
@@ -1573,64 +1609,6 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
});
}
@Test
@SuppressWarnings("null")
public void thingManagerWaitsWithInitializeUntilBundleProcessingIsFinished() throws Exception {
Thing thing = ThingBuilder.create(THING_TYPE_UID, THING_UID).build();
class ThingHandlerState {
@SuppressWarnings("unused")
@Nullable
ThingHandlerCallback callback;
}
final ThingHandlerState state = new ThingHandlerState();
ThingHandler thingHandler = mock(ThingHandler.class);
doAnswer(new Answer<Void>() {
@Override
public @Nullable Void answer(InvocationOnMock invocation) throws Throwable {
state.callback = (ThingHandlerCallback) invocation.getArgument(0);
return null;
}
}).when(thingHandler).setCallback(any(ThingHandlerCallback.class));
when(thingHandler.getThing()).thenReturn(thing);
ThingHandlerFactory thingHandlerFactory = mock(ThingHandlerFactory.class);
when(thingHandlerFactory.supportsThingType(any(ThingTypeUID.class))).thenReturn(true);
when(thingHandlerFactory.registerHandler(any(Thing.class))).thenReturn(thingHandler);
registerService(thingHandlerFactory);
final ReadyMarker marker = new ReadyMarker(ThingManagerImpl.XML_THING_TYPE,
ReadyMarkerUtils.getIdentifier(FrameworkUtil.getBundle(this.getClass())));
waitForAssert(() -> {
// wait for the XML processing to be finished, then remove the ready marker again
assertThat(readyService.isReady(marker), is(true));
readyService.unmarkReady(marker);
});
ThingStatusInfo uninitializedNone = ThingStatusInfoBuilder
.create(ThingStatus.UNINITIALIZED, ThingStatusDetail.NONE).build();
assertThat(thing.getStatusInfo(), is(uninitializedNone));
managedThingProvider.add(thing);
// just wait a little to make sure really nothing happens
Thread.sleep(1000);
verify(thingHandler, never()).initialize();
assertThat(thing.getStatusInfo(), is(uninitializedNone));
readyService.markReady(marker);
// ThingHandler.initialize() called, thing status is INITIALIZING.NONE
ThingStatusInfo initializingNone = ThingStatusInfoBuilder
.create(ThingStatus.INITIALIZING, ThingStatusDetail.NONE).build();
waitForAssert(() -> {
verify(thingHandler, times(1)).initialize();
assertThat(thing.getStatusInfo(), is(initializingNone));
});
}
@Test
@SuppressWarnings("null")
public void thingManagerCallsBridgeStatusChangedOnThingHandlerCorrectly() {
@@ -1719,6 +1697,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
}).when(thingHandlerFactory).registerHandler(any(Thing.class));
registerService(thingHandlerFactory);
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
managedThingProvider.add(bridge);
managedThingProvider.add(thing);
@@ -1859,6 +1839,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
}
}).when(thingHandlerFactory).registerHandler(any(Thing.class));
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
registerService(thingHandlerFactory);
managedThingProvider.add(bridge);
@@ -1985,6 +1967,8 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
}
}).when(thingHandlerFactory).registerHandler(any(Thing.class));
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
registerService(thingHandlerFactory);
managedThingProvider.add(bridge);
@@ -2016,6 +2000,9 @@ public class ThingManagerOSGiTest extends JavaOSGiTest {
}
final ThingHandlerState state = new ThingHandlerState();
registerThingTypeProvider();
registerConfigDescriptionProvider(false);
managedThingProvider.add(thing);
ThingHandler thingHandler = mock(ThingHandler.class);
@@ -16,8 +16,13 @@ import static java.util.Map.entry;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
@@ -44,10 +49,14 @@ import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.binding.ThingTypeProvider;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.events.ThingAddedEvent;
import org.openhab.core.thing.events.ThingRemovedEvent;
import org.openhab.core.thing.events.ThingUpdatedEvent;
import org.openhab.core.thing.type.ThingType;
import org.openhab.core.thing.type.ThingTypeBuilder;
import org.openhab.core.thing.type.ThingTypeRegistry;
import org.openhab.core.types.Command;
import org.osgi.framework.ServiceRegistration;
@@ -76,6 +85,7 @@ public class ThingRegistryOSGiTest extends JavaOSGiTest {
public void setUp() {
registerVolatileStorageService();
managedThingProvider = getService(ManagedThingProvider.class);
registerThingTypeProvider();
unregisterCurrentThingHandlerFactory();
}
@@ -246,4 +256,16 @@ public class ThingRegistryOSGiTest extends JavaOSGiTest {
thingHandlerFactoryServiceReg = null;
}
}
private void registerThingTypeProvider() {
ThingType thingType = ThingTypeBuilder.instance(THING_TYPE_UID, "label").build();
ThingTypeProvider thingTypeProvider = mock(ThingTypeProvider.class);
when(thingTypeProvider.getThingType(any(ThingTypeUID.class), nullable(Locale.class))).thenReturn(thingType);
registerService(thingTypeProvider);
ThingTypeRegistry thingTypeRegistry = mock(ThingTypeRegistry.class);
when(thingTypeRegistry.getThingType(any(ThingTypeUID.class))).thenReturn(thingType);
registerService(thingTypeRegistry);
}
}
@@ -0,0 +1,384 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.thing.internal.update;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.*;
import static org.openhab.core.thing.internal.ThingManagerImpl.PROPERTY_THING_TYPE_VERSION;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.service.ReadyService;
import org.openhab.core.test.SyntheticBundleInstaller;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ManagedThingProvider;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.binding.ThingTypeProvider;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeBuilder;
import org.openhab.core.thing.type.ChannelTypeProvider;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.thing.type.ThingType;
import org.openhab.core.thing.type.ThingTypeBuilder;
import org.openhab.core.thing.type.ThingTypeRegistry;
import org.openhab.core.types.Command;
import org.openhab.core.util.BundleResolver;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Tests for {@link ThingUpdateInstructionReader} and {@link ThingUpdateInstruction} implementations.
*
* @author Jan N. Klug - Initial contribution
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@NonNullByDefault
public class ThingUpdateOSGiTest extends JavaOSGiTest {
private static final String TEST_BUNDLE_NAME = "thingUpdateTest.bundle";
private static final String BINDING_ID = "testBinding";
private static final ThingTypeUID ADD_CHANNEL_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "testThingTypeAdd");
private static final ThingTypeUID UPDATE_CHANNEL_THING_TYPE_UID = new ThingTypeUID(BINDING_ID,
"testThingTypeUpdate");
private static final ThingTypeUID REMOVE_CHANNEL_THING_TYPE_UID = new ThingTypeUID(BINDING_ID,
"testThingTypeRemove");
private static final ThingTypeUID MULTIPLE_CHANNEL_THING_TYPE_UID = new ThingTypeUID(BINDING_ID,
"testThingTypeMultiple");
private static final String[] ADDED_TAGS = { "Tag1", "Tag2" };
private static final String THING_ID = "thing";
private @NonNullByDefault({}) Bundle testBundle;
private @NonNullByDefault({}) BundleResolver bundleResolver;
private @NonNullByDefault({}) ReadyService readyService;
private @NonNullByDefault({}) ThingRegistry thingRegistry;
private @NonNullByDefault({}) ManagedThingProvider managedThingProvider;
private @NonNullByDefault({}) TestThingHandlerFactory thingHandlerFactory;
@BeforeEach
public void beforeEach() throws Exception {
Hashtable<String, Object> properties = new Hashtable<>();
properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
bundleResolver = new BundleResolverImpl();
registerService(bundleResolver, BundleResolver.class.getName(), properties);
registerVolatileStorageService();
testBundle = SyntheticBundleInstaller.install(bundleContext, TEST_BUNDLE_NAME, "*.xml");
assertThat(testBundle, is(notNullValue()));
readyService = getService(ReadyService.class);
assertThat(readyService, is(notNullValue()));
thingRegistry = getService(ThingRegistry.class);
assertThat(thingRegistry, is(notNullValue()));
managedThingProvider = getService(ManagedThingProvider.class);
assertThat(managedThingProvider, is(notNullValue()));
thingHandlerFactory = new TestThingHandlerFactory();
registerService(thingHandlerFactory, ThingHandlerFactory.class.getName());
}
@AfterEach
public void afterEach() throws Exception {
testBundle.uninstall();
managedThingProvider.getAll().forEach(t -> managedThingProvider.remove(t.getUID()));
}
@Test
public void testSingleChannelAddition() {
registerThingType(ADD_CHANNEL_THING_TYPE_UID);
ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, "testChannelTypeId");
registerChannelTypes(channelTypeUID);
ThingUID thingUID = new ThingUID(ADD_CHANNEL_THING_TYPE_UID, THING_ID);
Thing thing = ThingBuilder.create(ADD_CHANNEL_THING_TYPE_UID, thingUID).build();
managedThingProvider.add(thing);
Thing updatedThing = assertThing(thing, 1);
assertThat(updatedThing.getChannels(), hasSize(3));
Channel channel1 = updatedThing.getChannel("testChannel1");
assertChannel(channel1, channelTypeUID, null, null);
Channel channel2 = updatedThing.getChannel("testChannel2");
assertChannel(channel2, channelTypeUID, "Test Label", null);
assertThat(channel2.getDefaultTags(), containsInAnyOrder(ADDED_TAGS));
Channel channel3 = updatedThing.getChannel("testChannel3");
assertChannel(channel3, channelTypeUID, "Test Label", "Test Description");
}
@Test
public void testSingleChannelUpdate() {
registerThingType(UPDATE_CHANNEL_THING_TYPE_UID);
ChannelTypeUID channelTypeOldUID = new ChannelTypeUID(BINDING_ID, "testChannelOldTypeId");
ChannelTypeUID channelTypeNewUID = new ChannelTypeUID(BINDING_ID, "testChannelNewTypeId");
registerChannelTypes(channelTypeOldUID, channelTypeNewUID);
ThingUID thingUID = new ThingUID(UPDATE_CHANNEL_THING_TYPE_UID, THING_ID);
ChannelUID channelUID1 = new ChannelUID(thingUID, "testChannel1");
ChannelUID channelUID2 = new ChannelUID(thingUID, "testChannel2");
Configuration channelConfig = new Configuration(Map.of("foo", "bar"));
Channel origChannel1 = ChannelBuilder.create(channelUID1).withType(channelTypeOldUID)
.withConfiguration(channelConfig).build();
Channel origChannel2 = ChannelBuilder.create(channelUID2).withType(channelTypeOldUID)
.withConfiguration(channelConfig).build();
Thing thing = ThingBuilder.create(UPDATE_CHANNEL_THING_TYPE_UID, thingUID)
.withChannels(origChannel1, origChannel2).build();
managedThingProvider.add(thing);
Thing updatedThing = assertThing(thing, 1);
assertThat(updatedThing.getChannels(), hasSize(2));
Channel channel1 = updatedThing.getChannel(channelUID1);
assertChannel(channel1, channelTypeNewUID, "New Test Label", null);
assertThat(channel1.getConfiguration(), is(channelConfig));
Channel channel2 = updatedThing.getChannel(channelUID2);
assertChannel(channel2, channelTypeNewUID, null, null);
assertThat(channel2.getConfiguration().getProperties(), is(anEmptyMap()));
}
@Test
public void testSingleChannelRemoval() {
registerThingType(REMOVE_CHANNEL_THING_TYPE_UID);
ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, "testChannelTypeId");
registerChannelTypes(channelTypeUID);
ThingUID thingUID = new ThingUID(REMOVE_CHANNEL_THING_TYPE_UID, THING_ID);
ChannelUID channelUID = new ChannelUID(thingUID, "testChannel");
Thing thing = ThingBuilder.create(REMOVE_CHANNEL_THING_TYPE_UID, thingUID)
.withChannel(ChannelBuilder.create(channelUID).withType(channelTypeUID).build()).build();
managedThingProvider.add(thing);
Thing updatedThing = assertThing(thing, 1);
assertThat(updatedThing.getChannels(), hasSize(0));
}
@Test
public void testMultipleChannelUpdates() {
registerThingType(MULTIPLE_CHANNEL_THING_TYPE_UID);
ChannelTypeUID channelTypeOldUID = new ChannelTypeUID(BINDING_ID, "testChannelOldTypeId");
ChannelTypeUID channelTypeNewUID = new ChannelTypeUID(BINDING_ID, "testChannelNewTypeId");
registerChannelTypes(channelTypeOldUID, channelTypeNewUID);
ThingUID thingUID = new ThingUID(MULTIPLE_CHANNEL_THING_TYPE_UID, THING_ID);
ChannelUID channelUID0 = new ChannelUID(thingUID, "testChannel0");
ChannelUID channelUID1 = new ChannelUID(thingUID, "testChannel1");
Thing thing = ThingBuilder.create(MULTIPLE_CHANNEL_THING_TYPE_UID, thingUID)
.withChannel(ChannelBuilder.create(channelUID0).withType(channelTypeOldUID).build())
.withChannel(ChannelBuilder.create(channelUID1).withType(channelTypeOldUID).build()).build();
managedThingProvider.add(thing);
Thing updatedThing = assertThing(thing, 3);
assertThat(updatedThing.getChannels(), hasSize(2));
Channel channel1 = updatedThing.getChannel("testChannel1");
assertChannel(channel1, channelTypeNewUID, "Test Label", null);
Channel channel2 = updatedThing.getChannel("testChannel2");
assertChannel(channel2, channelTypeOldUID, "TestLabel", null);
}
@Test
public void testOnlyMatchingInstructionsUpdate() {
registerThingType(MULTIPLE_CHANNEL_THING_TYPE_UID);
ChannelTypeUID channelTypeOldUID = new ChannelTypeUID(BINDING_ID, "testChannelOldTypeId");
registerChannelTypes(channelTypeOldUID);
ThingUID thingUID = new ThingUID(MULTIPLE_CHANNEL_THING_TYPE_UID, THING_ID);
ChannelUID channelUID0 = new ChannelUID(thingUID, "testChannel0");
ChannelUID channelUID1 = new ChannelUID(thingUID, "testChannel1");
Thing thing = ThingBuilder.create(MULTIPLE_CHANNEL_THING_TYPE_UID, thingUID)
.withChannel(ChannelBuilder.create(channelUID0).withType(channelTypeOldUID).build())
.withChannel(ChannelBuilder.create(channelUID1).withType(channelTypeOldUID).build())
.withProperty(PROPERTY_THING_TYPE_VERSION, "2").build();
managedThingProvider.add(thing);
Thing updatedThing = assertThing(thing, 3);
assertThat(updatedThing.getChannels(), hasSize(1));
Channel channel1 = updatedThing.getChannel("testChannel1");
assertChannel(channel1, channelTypeOldUID, null, null);
}
@Test
public void testNotModifiedIfHigherVersion() {
registerThingType(ADD_CHANNEL_THING_TYPE_UID);
ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, "testChannelTypeId");
registerChannelTypes(channelTypeUID);
ThingUID thingUID = new ThingUID(ADD_CHANNEL_THING_TYPE_UID, THING_ID);
Thing thing = ThingBuilder.create(ADD_CHANNEL_THING_TYPE_UID, thingUID)
.withProperty(PROPERTY_THING_TYPE_VERSION, "1").build();
managedThingProvider.add(thing);
waitForAssert(() -> assertThat(thing.getStatus(), is(ThingStatus.ONLINE)));
assertThat(thingRegistry.get(thingUID), is(sameInstance(thing)));
assertThat(thing.getChannels(), is(emptyCollectionOf(Channel.class)));
}
private Thing assertThing(Thing oldThing, int expectedNewThingTypeVersion) {
ThingUID thingUID = oldThing.getUID();
waitForAssert(() -> {
@Nullable
Thing updatedThing = thingRegistry.get(thingUID);
assertThat(updatedThing, is(not(sameInstance(oldThing))));
});
@Nullable
Thing updatedThing = thingRegistry.get(thingUID);
assertThat(updatedThing.getStatus(), is(ThingStatus.ONLINE));
// check thing type version is upgraded
String thingTypeVersion = updatedThing.getProperties().get(PROPERTY_THING_TYPE_VERSION);
assertThat(thingTypeVersion, is(Integer.toString(expectedNewThingTypeVersion)));
return updatedThing;
}
private void assertChannel(@Nullable Channel channel, ChannelTypeUID channelTypeUID, @Nullable String label,
@Nullable String description) {
assertThat(channel, is(notNullValue()));
assertThat(channel.getChannelTypeUID(), is(channelTypeUID));
if (label != null) {
assertThat(channel.getLabel(), is(label));
} else {
assertThat(channel.getLabel(), is(nullValue()));
}
if (description != null) {
assertThat(channel.getDescription(), is(description));
} else {
assertThat(channel.getDescription(), is(nullValue()));
}
}
private void registerThingType(ThingTypeUID thingTypeUID) {
ThingType thingType = ThingTypeBuilder.instance(thingTypeUID, "label").build();
ThingTypeProvider thingTypeProvider = mock(ThingTypeProvider.class);
when(thingTypeProvider.getThingType(eq(thingTypeUID), nullable(Locale.class))).thenReturn(thingType);
registerService(thingTypeProvider);
ThingTypeRegistry thingTypeRegistry = mock(ThingTypeRegistry.class);
when(thingTypeRegistry.getThingType(eq(thingTypeUID))).thenReturn(thingType);
registerService(thingTypeRegistry);
}
private void registerChannelTypes(ChannelTypeUID... channelTypeUIDs) {
ChannelTypeProvider channelTypeProvider = mock(ChannelTypeProvider.class);
ChannelTypeRegistry channelTypeRegistry = mock(ChannelTypeRegistry.class);
for (ChannelTypeUID channelTypeUID : channelTypeUIDs) {
ChannelType channelType = ChannelTypeBuilder.state(channelTypeUID, "label", "Number").build();
when(channelTypeProvider.getChannelType(eq(channelTypeUID), nullable(Locale.class)))
.thenReturn(channelType);
when(channelTypeRegistry.getChannelType(eq(channelTypeUID))).thenReturn(channelType);
}
registerService(channelTypeProvider);
registerService(channelTypeRegistry);
}
class TestThingHandlerFactory extends BaseThingHandlerFactory {
Logger logger = LoggerFactory.getLogger(TestThingHandlerFactory.class);
@Override
public void activate(final ComponentContext ctx) {
super.activate(ctx);
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return true;
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
return new BaseThingHandler(thing) {
@Override
public void initialize() {
updateStatus(ThingStatus.ONLINE);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
};
}
}
private class BundleResolverImpl implements BundleResolver {
@Override
public Bundle resolveBundle(@NonNullByDefault({}) Class<?> clazz) {
// return the test bundle if the class is TestThingHandlerFactory
if (clazz != null && clazz.equals(TestThingHandlerFactory.class)) {
return testBundle;
} else {
return FrameworkUtil.getBundle(clazz);
}
}
}
}
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="testBinding:testThingTypeMultiple">
<instruction-set targetVersion="1">
<update-channel id="testChannel1">
<type>testBinding:testChannelNewTypeId</type>
<label>Test Label</label>
</update-channel>
</instruction-set>
<instruction-set targetVersion="2">
<add-channel id="testChannel2">
<type>testBinding:testChannelOldTypeId</type>
<label>TestLabel</label>
</add-channel>
</instruction-set>
<instruction-set targetVersion="3">
<remove-channel id="testChannel0"/>
</instruction-set>
</thing-type>
</update:update-descriptions>
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="testBinding:testThingTypeAdd">
<instruction-set targetVersion="1">
<add-channel id="testChannel1">
<type>testBinding:testChannelTypeId</type>
</add-channel>
<add-channel id="testChannel2">
<type>testBinding:testChannelTypeId</type>
<label>Test Label</label>
<tags>
<tag>Tag1</tag>
<tag>Tag2</tag>
</tags>
</add-channel>
<add-channel id="testChannel3">
<type>testBinding:testChannelTypeId</type>
<label>Test Label</label>
<description>Test Description</description>
</add-channel>
</instruction-set>
</thing-type>
<thing-type uid="testBinding:testThingTypeRemove">
<instruction-set targetVersion="1">
<remove-channel id="testChannel"/>
</instruction-set>
</thing-type>
<thing-type uid="testBinding:testThingTypeUpdate">
<instruction-set targetVersion="1">
<update-channel id="testChannel1">
<type>testBinding:testChannelNewTypeId</type>
<label>New Test Label</label>
</update-channel>
<update-channel id="testChannel2" preserveConfiguration="false">
<type>testBinding:testChannelNewTypeId</type>
</update-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>