1818import static org .mockito .ArgumentMatchers .*;
1919import static org .mockito .Mockito .*;
2020
21+ import java .nio .charset .StandardCharsets ;
2122import java .util .ArrayList ;
2223import java .util .Collections ;
2324import java .util .List ;
2627import java .util .concurrent .ScheduledExecutorService ;
2728import java .util .concurrent .ScheduledThreadPoolExecutor ;
2829import java .util .concurrent .TimeUnit ;
30+ import java .util .concurrent .TimeoutException ;
2931
3032import org .eclipse .jdt .annotation .NonNullByDefault ;
3133import org .junit .jupiter .api .AfterEach ;
3234import org .junit .jupiter .api .BeforeEach ;
33- import org .junit .jupiter .api .Disabled ;
3435import org .junit .jupiter .api .Test ;
3536import org .junit .jupiter .api .extension .ExtendWith ;
3637import org .mockito .Mock ;
5152import org .openhab .binding .homie .internal .homie300 .PropertyHelper ;
5253import org .openhab .binding .mqtt .generic .ChannelState ;
5354import org .openhab .binding .mqtt .generic .tools .ChildMap ;
54- import org .openhab .binding .mqtt .generic .tools .WaitForTopicValue ;
5555import org .openhab .core .io .transport .mqtt .MqttBrokerConnection ;
5656import org .openhab .core .io .transport .mqtt .MqttConnectionObserver ;
5757import org .openhab .core .io .transport .mqtt .MqttConnectionState ;
58+ import org .openhab .core .io .transport .mqtt .MqttMessageSubscriber ;
5859import org .openhab .core .library .types .OnOffType ;
5960import org .openhab .core .library .types .QuantityType ;
6061import org .openhab .core .library .unit .SIUnits ;
6465 * A full implementation test, that starts the embedded MQTT broker and publishes a homie device tree.
6566 *
6667 * @author David Graeff - Initial contribution
68+ * @author Leo Siepel - Test stabilization refactoring
6769 */
6870@ ExtendWith (MockitoExtension .class )
6971@ MockitoSettings (strictness = Strictness .LENIENT )
@@ -72,6 +74,7 @@ public class HomieImplementationTest extends MqttOSGiTest {
7274 private static final String BASE_TOPIC = "homie" ;
7375 private static final String DEVICE_ID = ThingChannelConstants .TEST_HOMIE_THING .getId ();
7476 private static final String DEVICE_TOPIC = BASE_TOPIC + "/" + DEVICE_ID ;
77+ private static final int MQTT_TIMEOUT = 10000 ;
7578
7679 private @ NonNullByDefault ({}) MqttBrokerConnection homieConnection ;
7780 private int registeredTopics = 100 ;
@@ -165,15 +168,21 @@ public void retrieveAllTopics() throws Exception {
165168 "Connection " + homieConnection .getClientId () + " not retrieving all topics " );
166169 }
167170
168- @ Disabled ("https://github.com/openhab/openhab-addons/issues/12667" )
169171 @ Test
170172 public void retrieveOneAttribute () throws Exception {
171- WaitForTopicValue watcher = new WaitForTopicValue (homieConnection , DEVICE_TOPIC + "/$homie" );
172- assertThat (watcher .waitForTopicValue (1000 ), is ("3.0" ));
173+ String topic = DEVICE_TOPIC + "/$homie" ;
174+ CompletableFuture <String > receivedValue = new CompletableFuture <>();
175+ MqttMessageSubscriber subscriber = (receivedTopic , payload ) -> receivedValue
176+ .complete (new String (payload , StandardCharsets .UTF_8 ));
177+ try {
178+ assertTrue (homieConnection .subscribe (topic , subscriber ).get (5 , TimeUnit .SECONDS ));
179+ assertThat (receivedValue .get (MQTT_TIMEOUT , TimeUnit .MILLISECONDS ), is ("3.0" ));
180+ } finally {
181+ homieConnection .unsubscribe (topic , subscriber ).get (5 , TimeUnit .SECONDS );
182+ }
173183 }
174184
175185 @ SuppressWarnings ("null" )
176- @ Disabled ("Temporarily disabled: unstable" )
177186 @ Test
178187 public void retrieveAttributes () throws Exception {
179188 assertThat (homieConnection .hasSubscribers (), is (false ));
@@ -183,9 +192,6 @@ public void retrieveAttributes() throws Exception {
183192 Property property = spy (
184193 new Property (DEVICE_TOPIC + "/testnode" , node , "temperature" , callback , new PropertyAttributes ()));
185194
186- // Create a scheduler
187- ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor (4 );
188-
189195 property .subscribe (homieConnection , scheduler , 500 ).get ();
190196
191197 assertThat (property .attributes .settable , is (true ));
@@ -202,8 +208,8 @@ public void retrieveAttributes() throws Exception {
202208
203209 property .startChannel (homieConnection , scheduler , 500 ).get ();
204210 verify (channelState ).start (any (), any (), anyInt ());
205- verify (channelState , timeout (500 )).processMessage (any (), any ());
206- verify (callback ). updateChannelState (any (), any ());
211+ verify (callback , timeout (MQTT_TIMEOUT )).updateChannelState (any (), any ());
212+ verify (channelState ). processMessage (any (), any ());
207213
208214 assertThat (property .getChannelState ().getCache ().getChannelState (),
209215 is (new QuantityType <>(10 , SIUnits .CELSIUS )));
@@ -231,7 +237,6 @@ public Node createSpyNode(InvocationOnMock invocation) {
231237 }
232238
233239 @ SuppressWarnings ("null" )
234- @ Disabled ("Temporarily disabled: unstable" )
235240 @ Test
236241 public void parseHomieTree () throws Exception {
237242 // Create a Homie Device object. Because spied Nodes are required for call verification,
@@ -297,21 +302,34 @@ public void parseHomieTree() throws Exception {
297302 device .startChannels (homieConnection , scheduler , 50 , handler ).get ();
298303 assertThat (propertyBell .getChannelState ().isStateful (), is (false ));
299304 assertThat (propertyBell .getChannelState ().getCache ().getChannelState (), is (UnDefType .UNDEF ));
300- assertThat (property .getChannelState ().getCache ().getChannelState (),
301- is (new QuantityType <>(10 , SIUnits .CELSIUS )));
305+ Property temperatureProperty = property ;
306+ waitForAssert (() -> assertThat (temperatureProperty .getChannelState ().getCache ().getChannelState (),
307+ is (new QuantityType <>(10 , SIUnits .CELSIUS ))));
302308
303309 property = node .properties .get ("testRetain" );
304- WaitForTopicValue watcher = new WaitForTopicValue (brokerConnection , propertyTestTopic + "/set" );
305- // Watch the topic. Publish a retain=false value to MQTT
306- property .getChannelState ().publishValue (OnOffType .OFF ).get ();
307- assertThat (watcher .waitForTopicValue (10000 ), is ("false" ));
310+ String commandTopic = propertyTestTopic + "/set" ;
311+ CompletableFuture <String > receivedValue = new CompletableFuture <>();
312+ MqttMessageSubscriber subscriber = (topic , payload ) -> receivedValue
313+ .complete (new String (payload , StandardCharsets .UTF_8 ));
314+ try {
315+ assertTrue (brokerConnection .subscribe (commandTopic , subscriber ).get (5 , TimeUnit .SECONDS ));
316+ property .getChannelState ().publishValue (OnOffType .OFF ).get ();
317+ assertThat (receivedValue .get (MQTT_TIMEOUT , TimeUnit .MILLISECONDS ), is ("false" ));
318+ } finally {
319+ brokerConnection .unsubscribe (commandTopic , subscriber ).get (5 , TimeUnit .SECONDS );
320+ }
308321
309322 // Publish a retain=false value to MQTT.
310323 property .getChannelState ().publishValue (OnOffType .ON ).get ();
311324 // No value is expected to be retained on this MQTT topic
312- waitForAssert (() -> {
313- WaitForTopicValue w = new WaitForTopicValue (brokerConnection , propertyTestTopic + "/set" );
314- assertNull (w .waitForTopicValue (50 ));
315- }, 500 , 100 );
325+ CompletableFuture <String > retainedValue = new CompletableFuture <>();
326+ MqttMessageSubscriber retainedSubscriber = (topic , payload ) -> retainedValue
327+ .complete (new String (payload , StandardCharsets .UTF_8 ));
328+ try {
329+ assertTrue (brokerConnection .subscribe (commandTopic , retainedSubscriber ).get (5 , TimeUnit .SECONDS ));
330+ assertThrows (TimeoutException .class , () -> retainedValue .get (1 , TimeUnit .SECONDS ));
331+ } finally {
332+ brokerConnection .unsubscribe (commandTopic , retainedSubscriber ).get (5 , TimeUnit .SECONDS );
333+ }
316334 }
317335}
0 commit comments