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.io.hueemulation.internal.upnp;
15 import static org.hamcrest.CoreMatchers.is;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.assertEquals;
18 import static org.mockito.Mockito.mock;
20 import java.io.IOException;
21 import java.net.DatagramPacket;
22 import java.net.DatagramSocket;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.Executor;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.TimeoutException;
28 import javax.ws.rs.core.Response;
30 import org.glassfish.grizzly.osgi.httpservice.HttpServiceImpl;
31 import org.glassfish.grizzly.osgi.httpservice.OSGiMainHandler;
32 import org.glassfish.grizzly.osgi.httpservice.util.Logger;
33 import org.glassfish.jersey.server.ResourceConfig;
34 import org.hamcrest.CoreMatchers;
35 import org.junit.jupiter.api.AfterAll;
36 import org.junit.jupiter.api.AfterEach;
37 import org.junit.jupiter.api.BeforeAll;
38 import org.junit.jupiter.api.BeforeEach;
39 import org.junit.jupiter.api.Test;
40 import org.mockito.ArgumentMatchers;
41 import org.mockito.Mockito;
42 import org.openhab.io.hueemulation.internal.rest.CommonSetup;
43 import org.openhab.io.hueemulation.internal.rest.LightsAndGroups;
44 import org.osgi.framework.Bundle;
45 import org.osgi.util.tracker.ServiceTracker;
48 * Tests the upnp server part if the description.xml is available and if the udp thread comes online
50 * @author David Graeff - Initial contribution
52 public class UpnpTests {
53 protected static CommonSetup commonSetup = null;
54 protected UpnpServer subject;
55 protected static OSGiMainHandler mainHttpHandler;
56 private static HttpServiceImpl httpServiceImpl;
57 private static String descriptionPath;
59 LightsAndGroups lightsAndGroups = new LightsAndGroups();
62 public static void setupHttpParts() throws IOException {
63 commonSetup = new CommonSetup(true);
64 commonSetup.start(new ResourceConfig());
66 descriptionPath = commonSetup.basePath.replace("/api", "/description.xml");
68 Logger logger = new org.glassfish.grizzly.osgi.httpservice.util.Logger(mock(ServiceTracker.class));
70 mainHttpHandler = new OSGiMainHandler(logger, mock(Bundle.class));
71 commonSetup.server.getServerConfiguration().addHttpHandler(mainHttpHandler, "/");
73 httpServiceImpl = new HttpServiceImpl(mock(Bundle.class), logger);
78 Executor executor = mock(Executor.class);
79 Mockito.doAnswer(a -> {
80 ((Runnable) a.getArgument(0)).run();
82 }).when(executor).execute(ArgumentMatchers.any());
83 subject = new UpnpServer(executor);
84 subject.httpService = httpServiceImpl;
85 subject.cs = commonSetup.cs;
86 subject.overwriteReadyToFalse = true;
87 subject.activate(); // don't execute handleEvent()
88 subject.overwriteReadyToFalse = false;
92 public void tearDown() {
97 public static void tearDownHttp() {
98 mainHttpHandler.unregisterAll();
99 commonSetup.dispose();
103 public void descriptionWithoutAddress() {
104 Response response = commonSetup.client.target(descriptionPath).request().get();
105 assertEquals(404, response.getStatus());
109 public void descriptionWithAddress()
110 throws InterruptedException, ExecutionException, TimeoutException, IOException {
111 HueEmulationConfigWithRuntime r = subject.createConfiguration(null);
112 r = subject.performAddressTest(r);
113 subject.applyConfiguration(r);
114 Response response = commonSetup.client.target(descriptionPath).request().get();
115 assertEquals(200, response.getStatus());
116 String body = response.readEntity(String.class);
117 assertThat(body, is(subject.xmlDocWithAddress));
120 throw new IllegalStateException();
123 // UDP thread started?
124 r.startNow(r).get(5, TimeUnit.SECONDS);
125 assertThat(subject.upnpAnnouncementThreadRunning(), is(true));
127 // Send M-SEARCH UPNP "packet" and check if the result contains our bridge ID
128 try (DatagramSocket sendSocket = new DatagramSocket()) {
129 sendSocket.setSoTimeout(700);
130 byte[] bytes = "M-SEARCH".getBytes();
131 sendSocket.send(new DatagramPacket(bytes, bytes.length, subject.MULTI_ADDR_IPV4, UpnpServer.UPNP_PORT));
132 byte[] buffer = new byte[1000];
133 DatagramPacket p = new DatagramPacket(buffer, buffer.length);
134 sendSocket.receive(p);
135 String received = new String(buffer);
136 assertThat(received, CoreMatchers.startsWith("HTTP/1.1 200 OK"));
137 assertThat(received, CoreMatchers.containsString("hue-bridgeid: DEMOUUID"));
141 assertThat(subject.upnpAnnouncementThreadRunning(), is(false));
145 public void handEventTest() throws InterruptedException, ExecutionException, TimeoutException {
146 subject.handleEvent(null);
147 subject.configChangeFuture.get(5, TimeUnit.SECONDS);
148 assertThat(subject.upnpAnnouncementThreadRunning(), is(true));
150 subject.deactivate();
151 assertThat(subject.upnpAnnouncementThreadRunning(), is(false));