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.upnpcontrol.internal.handler;
15 import static org.eclipse.jdt.annotation.Checks.requireNonNull;
16 import static org.mockito.ArgumentMatchers.*;
17 import static org.mockito.Mockito.*;
20 import java.nio.file.Path;
21 import java.util.HashMap;
23 import java.util.concurrent.Executors;
24 import java.util.concurrent.ScheduledExecutorService;
25 import java.util.concurrent.TimeUnit;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.junit.jupiter.api.extension.ExtendWith;
30 import org.junit.jupiter.api.io.TempDir;
31 import org.mockito.Mock;
32 import org.mockito.junit.jupiter.MockitoExtension;
33 import org.mockito.junit.jupiter.MockitoSettings;
34 import org.mockito.quality.Strictness;
35 import org.openhab.binding.upnpcontrol.internal.UpnpDynamicCommandDescriptionProvider;
36 import org.openhab.binding.upnpcontrol.internal.UpnpDynamicStateDescriptionProvider;
37 import org.openhab.binding.upnpcontrol.internal.config.UpnpControlBindingConfiguration;
38 import org.openhab.binding.upnpcontrol.internal.config.UpnpControlConfiguration;
39 import org.openhab.core.config.core.Configuration;
40 import org.openhab.core.io.transport.upnp.UpnpIOService;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingStatus;
43 import org.openhab.core.thing.binding.ThingHandlerCallback;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * Base class for {@link UpnpServerHandlerTest} and {@link UpnpRendererHandlerTest}.
50 * @author Mark Herwege - Initial contribution
52 @SuppressWarnings({ "null" })
53 @ExtendWith(MockitoExtension.class)
54 @MockitoSettings(strictness = Strictness.LENIENT)
56 public class UpnpHandlerTest {
58 private final Logger logger = LoggerFactory.getLogger(UpnpHandlerTest.class);
60 private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
62 protected @Nullable UpnpHandler handler;
65 protected @Nullable Thing thing;
68 protected @Nullable UpnpIOService upnpIOService;
71 protected @Nullable UpnpDynamicStateDescriptionProvider upnpStateDescriptionProvider;
74 protected @Nullable UpnpDynamicCommandDescriptionProvider upnpCommandDescriptionProvider;
76 protected UpnpControlBindingConfiguration configuration = new UpnpControlBindingConfiguration();
79 protected @Nullable Configuration config;
81 // Use temporary folder for favorites and playlists testing
83 public @Nullable Path tempFolder;
87 protected ScheduledExecutorService scheduler;
90 protected @Nullable ThingHandlerCallback callback;
93 // don't test for multi-threading, so avoid using extra threads
94 implementAsDirectExecutor(requireNonNull(scheduler));
96 String path = tempFolder.toString();
97 if (!(path.endsWith(File.separator) || path.endsWith("/"))) {
98 path = path + File.separator;
100 configuration.path = path;
102 // stub thing methods
103 when(thing.getConfiguration()).thenReturn(requireNonNull(config));
104 when(thing.getStatus()).thenReturn(ThingStatus.OFFLINE);
106 // stub upnpIOService methods for initialize
107 when(upnpIOService.isRegistered(any())).thenReturn(true);
109 Map<String, String> result = new HashMap<>();
110 result.put("ConnectionID", "0");
111 result.put("AVTransportID", "0");
112 result.put("RcsID", "0");
113 when(upnpIOService.invokeAction(any(), eq("ConnectionManager"), eq("GetCurrentConnectionInfo"), anyMap()))
116 // stub config for initialize
117 when(config.as(UpnpControlConfiguration.class)).thenReturn(new UpnpControlConfiguration());
120 protected void initHandler(UpnpHandler handler) {
121 handler.setCallback(callback);
122 handler.upnpScheduler = requireNonNull(scheduler);
124 // No timeouts for responses, as we don't actually communicate with a UPnP device
125 handler.config.responseTimeout = 0;
127 doReturn("12345").when(handler).getUDN();
131 * Mock the {@link ScheduledExecutorService}, so all testing is done in the current thread. We do not test
132 * request/response with a real media server, so do not need the executor to avoid long running processes.
133 * As an exception, we will schedule one off futures with 500ms delay, as this is related to internal
139 private void implementAsDirectExecutor(ScheduledExecutorService executor) {
140 doAnswer(invocation -> {
141 ((Runnable) invocation.getArguments()[0]).run();
143 }).when(executor).submit(any(Runnable.class));
144 doAnswer(invocation -> {
145 ((Runnable) invocation.getArguments()[0]).run();
147 }).when(executor).scheduleWithFixedDelay(any(Runnable.class), eq(0L), anyLong(), any(TimeUnit.class));
148 doAnswer(invocation -> {
149 return SCHEDULER.schedule((Runnable) invocation.getArguments()[0], 500, TimeUnit.MILLISECONDS);
150 }).when(executor).schedule(any(Runnable.class), anyLong(), any(TimeUnit.class));
153 public void tearDown() {
154 logger.info("-----------------------------------------------------------------------------------");