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.mqtt.ruuvigateway.internal.discovery;
15 import static org.hamcrest.CoreMatchers.is;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.assertTrue;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Objects;
22 import java.util.concurrent.CopyOnWriteArrayList;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.concurrent.TimeUnit;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.junit.jupiter.api.extension.ExtendWith;
31 import org.junit.jupiter.params.ParameterizedTest;
32 import org.junit.jupiter.params.provider.ValueSource;
33 import org.mockito.Mock;
34 import org.mockito.junit.jupiter.MockitoExtension;
35 import org.openhab.binding.mqtt.MqttBindingConstants;
36 import org.openhab.binding.mqtt.discovery.MQTTTopicDiscoveryService;
37 import org.openhab.binding.mqtt.ruuvigateway.internal.RuuviGatewayBindingConstants;
38 import org.openhab.core.config.discovery.DiscoveryListener;
39 import org.openhab.core.config.discovery.DiscoveryResult;
40 import org.openhab.core.config.discovery.DiscoveryService;
41 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
42 import org.openhab.core.thing.Thing;
43 import org.openhab.core.thing.ThingTypeUID;
44 import org.openhab.core.thing.ThingUID;
47 * Tests for {@link RuuviGatewayDiscoveryService}
49 * @author Anton Kharuzhy - Initial contribution
50 * @author Sami Salonen - Adapted from Home Assistant to Ruuvi Gateway tests
52 @ExtendWith(MockitoExtension.class)
54 public class RuuviGatewayDiscoveryTests {
55 private @NonNullByDefault({}) RuuviGatewayDiscoveryService discovery;
56 private static final ThingUID MQTT_BRIDGE_UID = new ThingUID(MqttBindingConstants.BRIDGE_TYPE_BROKER, "broker");
58 private @Mock @NonNullByDefault({}) MQTTTopicDiscoveryService mqttTopicDiscoveryService;
59 private @Mock @NonNullByDefault({}) MqttBrokerConnection mqttConnection;
62 public void beforeEach() {
63 discovery = new RuuviGatewayDiscoveryService(mqttTopicDiscoveryService);
67 @ValueSource(strings = { "de:ea:DB:be:ff:00", "de:ea:DB:be:ff-00", "de-ea-DB-be-ff-00" })
68 public void testDiscoveryMacFormatPermutations(String leafTopic) throws Exception {
69 var discoveryListener = new LatchDiscoveryListener();
70 var latch = discoveryListener.createWaitForThingsDiscoveredLatch(1);
72 // When discover one thing with two channels
73 discovery.addDiscoveryListener(discoveryListener);
74 discovery.receivedMessage(MQTT_BRIDGE_UID, mqttConnection, "ruuvi/foo/bar/" + leafTopic, "{}".getBytes());
76 // Then one thing found
77 assertTrue(latch.await(3, TimeUnit.SECONDS));
78 var discoveryResults = discoveryListener.getDiscoveryResults();
79 assertThat(discoveryResults.size(), is(1));
81 DiscoveryResult result = discoveryResults.get(0);
82 Objects.requireNonNull(result); // Make compiler happy
83 assertThat(result.getBridgeUID(), is(MQTT_BRIDGE_UID));
84 assertThat(result.getProperties().get(Thing.PROPERTY_VENDOR), is("Ruuvi Innovations Ltd (Oy)"));
85 assertThat(result.getProperties().get(RuuviGatewayBindingConstants.PROPERTY_TAG_ID), is("DE:EA:DB:BE:FF:00"));
86 assertThat(result.getProperties().get(RuuviGatewayBindingConstants.CONFIGURATION_PROPERTY_TOPIC),
87 is("ruuvi/foo/bar/" + leafTopic));
91 public void testDiscoveryMultipleThings() throws Exception {
92 var discoveryListener = new LatchDiscoveryListener();
93 var latch = discoveryListener.createWaitForThingsDiscoveredLatch(2);
95 discovery.addDiscoveryListener(discoveryListener);
96 discovery.receivedMessage(MQTT_BRIDGE_UID, mqttConnection, "something/to/ignore/ruuvi/foo/bar/invalid:mac",
98 discovery.receivedMessage(MQTT_BRIDGE_UID, mqttConnection, "ruuvi/foo/bar/invalid:mac", "{}".getBytes());
99 discovery.receivedMessage(MQTT_BRIDGE_UID, mqttConnection, "ruuvi/foo/bar/aa:bb", "{}".getBytes()); // too short
101 discovery.receivedMessage(MQTT_BRIDGE_UID, mqttConnection, "ruuvi/foo/bar/de:ea:DB:be:ff:00", "{}".getBytes());
102 discovery.receivedMessage(MQTT_BRIDGE_UID, mqttConnection, "ruuvi/foo/bar/de:ea:DB:be:ff:01", "{}".getBytes());
104 // Then one thing found
105 assertTrue(latch.await(3, TimeUnit.SECONDS));
106 var discoveryResults = discoveryListener.getDiscoveryResults();
107 assertThat(discoveryResults.size(), is(2));
109 assertTrue(discoveryResults.stream().allMatch(result -> {
110 assertThat(result.getBridgeUID(), is(MQTT_BRIDGE_UID));
111 assertThat(result.getProperties().get(Thing.PROPERTY_VENDOR), is("Ruuvi Innovations Ltd (Oy)"));
116 discoveryResults.stream().anyMatch(result -> {
117 return "DE:EA:DB:BE:FF:00"
118 .equals(result.getProperties().get(RuuviGatewayBindingConstants.PROPERTY_TAG_ID))
119 && "ruuvi/foo/bar/de:ea:DB:be:ff:00".equals(result.getProperties()
120 .get(RuuviGatewayBindingConstants.CONFIGURATION_PROPERTY_TOPIC));
122 discoveryResults.stream().anyMatch(result -> {
123 return "DE:EA:DB:BE:FF:01"
124 .equals(result.getProperties().get(RuuviGatewayBindingConstants.PROPERTY_TAG_ID))
125 && "ruuvi/foo/bar/de:ea:DB:be:ff:01".equals(result.getProperties()
126 .get(RuuviGatewayBindingConstants.CONFIGURATION_PROPERTY_TOPIC));
129 , "Failed to match: " + discoveryResults.toString());
132 private static class LatchDiscoveryListener implements DiscoveryListener {
133 private final CopyOnWriteArrayList<DiscoveryResult> discoveryResults = new CopyOnWriteArrayList<>();
134 private @Nullable CountDownLatch latch;
137 public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
138 discoveryResults.add(result);
139 CountDownLatch localLatch = latch;
140 if (localLatch != null) {
141 localLatch.countDown();
146 public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
150 public @Nullable Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
151 @Nullable Collection<ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
152 return Collections.emptyList();
155 public CopyOnWriteArrayList<DiscoveryResult> getDiscoveryResults() {
156 return discoveryResults;
159 public CountDownLatch createWaitForThingsDiscoveredLatch(int count) {
160 final var newLatch = new CountDownLatch(count);