]> git.basschouten.com Git - openhab-addons.git/blob
fdc5d8cfe15567cde2d35a9488ca896d88044c95
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mqtt.ruuvigateway.internal.discovery;
14
15 import static org.hamcrest.CoreMatchers.is;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.assertTrue;
18
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;
25
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;
45
46 /**
47  * Tests for {@link RuuviGatewayDiscoveryService}
48  *
49  * @author Anton Kharuzhy - Initial contribution
50  * @author Sami Salonen - Adapted from Home Assistant to Ruuvi Gateway tests
51  */
52 @ExtendWith(MockitoExtension.class)
53 @NonNullByDefault
54 public class RuuviGatewayDiscoveryTests {
55     private @NonNullByDefault({}) RuuviGatewayDiscoveryService discovery;
56     private static final ThingUID MQTT_BRIDGE_UID = new ThingUID(MqttBindingConstants.BRIDGE_TYPE_BROKER, "broker");
57
58     private @Mock @NonNullByDefault({}) MQTTTopicDiscoveryService mqttTopicDiscoveryService;
59     private @Mock @NonNullByDefault({}) MqttBrokerConnection mqttConnection;
60
61     @BeforeEach
62     public void beforeEach() {
63         discovery = new RuuviGatewayDiscoveryService(mqttTopicDiscoveryService);
64     }
65
66     @ParameterizedTest
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);
71
72         // When discover one thing with two channels
73         discovery.addDiscoveryListener(discoveryListener);
74         discovery.receivedMessage(MQTT_BRIDGE_UID, mqttConnection, "ruuvi/foo/bar/" + leafTopic, "{}".getBytes());
75
76         // Then one thing found
77         assertTrue(latch.await(3, TimeUnit.SECONDS));
78         var discoveryResults = discoveryListener.getDiscoveryResults();
79         assertThat(discoveryResults.size(), is(1));
80         @Nullable
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));
88     }
89
90     @Test
91     public void testDiscoveryMultipleThings() throws Exception {
92         var discoveryListener = new LatchDiscoveryListener();
93         var latch = discoveryListener.createWaitForThingsDiscoveredLatch(2);
94
95         discovery.addDiscoveryListener(discoveryListener);
96         discovery.receivedMessage(MQTT_BRIDGE_UID, mqttConnection, "something/to/ignore/ruuvi/foo/bar/invalid:mac",
97                 "{}".getBytes());
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
100                                                                                                             // mac
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());
103
104         // Then one thing found
105         assertTrue(latch.await(3, TimeUnit.SECONDS));
106         var discoveryResults = discoveryListener.getDiscoveryResults();
107         assertThat(discoveryResults.size(), is(2));
108
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)"));
112             return true;
113         }));
114
115         assertTrue(//
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));
121                 }) && //
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));
127                         })
128
129                 , "Failed to match: " + discoveryResults.toString());
130     }
131
132     private static class LatchDiscoveryListener implements DiscoveryListener {
133         private final CopyOnWriteArrayList<DiscoveryResult> discoveryResults = new CopyOnWriteArrayList<>();
134         private @Nullable CountDownLatch latch;
135
136         @Override
137         public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
138             discoveryResults.add(result);
139             CountDownLatch localLatch = latch;
140             if (localLatch != null) {
141                 localLatch.countDown();
142             }
143         }
144
145         @Override
146         public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
147         }
148
149         @Override
150         public @Nullable Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
151                 @Nullable Collection<ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
152             return Collections.emptyList();
153         }
154
155         public CopyOnWriteArrayList<DiscoveryResult> getDiscoveryResults() {
156             return discoveryResults;
157         }
158
159         public CountDownLatch createWaitForThingsDiscoveredLatch(int count) {
160             final var newLatch = new CountDownLatch(count);
161             latch = newLatch;
162             return newLatch;
163         }
164     }
165 }