1+ /*
2+ * Copyright 2018 dc-square and the HiveMQ MQTT Client Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ *
16+ */
17+
18+ package com .hivemq .client .internal .mqtt .datatypes ;
19+
20+ import org .jetbrains .annotations .NotNull ;
21+ import org .junit .jupiter .api .Test ;
22+ import org .junit .jupiter .params .ParameterizedTest ;
23+ import org .junit .jupiter .params .provider .CsvSource ;
24+
25+ import java .util .NoSuchElementException ;
26+
27+ import static org .junit .jupiter .api .Assertions .*;
28+
29+ /**
30+ * @author Silvio Giebl
31+ */
32+ class MqttTopicIteratorTest {
33+
34+ @ Test
35+ void of_topic () {
36+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicImpl .of ("test/topic" ));
37+
38+ assertTrue (topicIterator .hasNext ());
39+ final MqttTopicLevel level1 = topicIterator .next ();
40+ assertEquals (MqttTopicLevel .of ("test" .getBytes (), 0 , 4 ), level1 );
41+ assertFalse (level1 .isSingleLevelWildcard ());
42+
43+ assertTrue (topicIterator .hasNext ());
44+ final MqttTopicLevel level2 = topicIterator .next ();
45+ assertEquals (MqttTopicLevel .of ("topic" .getBytes (), 0 , 5 ), level2 );
46+ assertFalse (level2 .isSingleLevelWildcard ());
47+
48+ assertFalse (topicIterator .hasNext ());
49+ assertFalse (topicIterator .hasMultiLevelWildcard ());
50+ assertThrows (NoSuchElementException .class , topicIterator ::next );
51+ }
52+
53+ @ Test
54+ void of_topicFilter () {
55+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of ("test/+/topic/#" ));
56+
57+ assertTrue (topicIterator .hasNext ());
58+ final MqttTopicLevel level1 = topicIterator .next ();
59+ assertEquals (MqttTopicLevel .of ("test" .getBytes (), 0 , 4 ), level1 );
60+ assertFalse (level1 .isSingleLevelWildcard ());
61+
62+ assertTrue (topicIterator .hasNext ());
63+ final MqttTopicLevel level2 = topicIterator .next ();
64+ assertEquals (MqttTopicLevel .of ("+" .getBytes (), 0 , 1 ), level2 );
65+ assertTrue (level2 .isSingleLevelWildcard ());
66+
67+ assertTrue (topicIterator .hasNext ());
68+ final MqttTopicLevel level3 = topicIterator .next ();
69+ assertEquals (MqttTopicLevel .of ("topic" .getBytes (), 0 , 5 ), level3 );
70+ assertFalse (level3 .isSingleLevelWildcard ());
71+
72+ assertFalse (topicIterator .hasNext ());
73+ assertTrue (topicIterator .hasMultiLevelWildcard ());
74+ assertThrows (NoSuchElementException .class , topicIterator ::next );
75+ }
76+
77+ @ Test
78+ void fork () {
79+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of ("test/+/topic/#" ));
80+ topicIterator .next ();
81+
82+ final MqttTopicIterator fork = topicIterator .fork ();
83+
84+ assertTrue (topicIterator .hasNext ());
85+ final MqttTopicLevel level2 = topicIterator .next ();
86+ assertEquals (MqttTopicLevel .of ("+" .getBytes (), 0 , 1 ), level2 );
87+ assertTrue (level2 .isSingleLevelWildcard ());
88+ assertTrue (topicIterator .hasNext ());
89+ final MqttTopicLevel level3 = topicIterator .next ();
90+ assertEquals (MqttTopicLevel .of ("topic" .getBytes (), 0 , 5 ), level3 );
91+ assertFalse (level3 .isSingleLevelWildcard ());
92+ assertFalse (topicIterator .hasNext ());
93+ assertTrue (topicIterator .hasMultiLevelWildcard ());
94+ assertThrows (NoSuchElementException .class , topicIterator ::next );
95+
96+ assertTrue (fork .hasNext ());
97+ final MqttTopicLevel forkLevel2 = fork .next ();
98+ assertEquals (MqttTopicLevel .of ("+" .getBytes (), 0 , 1 ), forkLevel2 );
99+ assertTrue (forkLevel2 .isSingleLevelWildcard ());
100+ assertTrue (fork .hasNext ());
101+ final MqttTopicLevel forkLevel3 = fork .next ();
102+ assertEquals (MqttTopicLevel .of ("topic" .getBytes (), 0 , 5 ), forkLevel3 );
103+ assertFalse (forkLevel3 .isSingleLevelWildcard ());
104+ assertFalse (fork .hasNext ());
105+ assertTrue (fork .hasMultiLevelWildcard ());
106+ assertThrows (NoSuchElementException .class , fork ::next );
107+ }
108+
109+ @ ParameterizedTest
110+ @ CsvSource ({"test/topic, topic" , "test/+, +" , "test/+/#, +" })
111+ void trim_topicLevel (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
112+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
113+ topicIterator .next ();
114+
115+ final MqttTopicLevel trim = topicIterator .next ().trim ();
116+ final MqttTopicLevel level = MqttTopicLevel .of (topicLevels .getBytes (), 0 , topicLevels .getBytes ().length );
117+ MqttTopicLevelsTest .assertEqualsWithClass (level , trim );
118+ }
119+
120+ @ ParameterizedTest
121+ @ CsvSource ({"test/+/topic, +/topic" , "test/+/topic/#, +/topic" })
122+ void trim_topicLevels (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
123+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
124+ topicIterator .next ();
125+
126+ final MqttTopicLevel trim = topicIterator .next ().trim ();
127+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
128+ MqttTopicLevelsTest .assertEqualsWithClass (levels , trim );
129+ }
130+
131+ @ ParameterizedTest
132+ @ CsvSource ({"test/topic/filter, test/topic/filter" , "test/+/topic, test/+/topic" , "test/+/topic/#, test/+/topic" })
133+ void forwardIfEqual_equalToEnd (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
134+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
135+ topicIterator .next ();
136+
137+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
138+ assertTrue (topicIterator .forwardIfEqual (levels ));
139+
140+ assertFalse (topicIterator .hasNext ());
141+ assertEquals (topicFilter .endsWith ("#" ), topicIterator .hasMultiLevelWildcard ());
142+ }
143+
144+ @ ParameterizedTest
145+ @ CsvSource ({
146+ "test/topic/filter/abc, test/topic/filter" , "test/+/topic/abc, test/+/topic" ,
147+ "test/+/topic/abc/#, test/+/topic"
148+ })
149+ void forwardIfEqual_remainingLevels (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
150+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
151+ topicIterator .next ();
152+
153+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
154+ assertTrue (topicIterator .forwardIfEqual (levels ));
155+
156+ assertTrue (topicIterator .hasNext ());
157+ final MqttTopicLevel lastLevel = topicIterator .next ();
158+ assertEquals (MqttTopicLevel .of ("abc" .getBytes (), 0 , 3 ), lastLevel );
159+ assertFalse (lastLevel .isSingleLevelWildcard ());
160+ assertFalse (topicIterator .hasNext ());
161+ assertEquals (topicFilter .endsWith ("#" ), topicIterator .hasMultiLevelWildcard ());
162+ }
163+
164+ @ ParameterizedTest
165+ @ CsvSource ({
166+ "test/topic/filter, test/topic/filter/abc" , "test/+/topic, test/+/topic/abc" ,
167+ "test/+/topic/#, test/+/topic/abc"
168+ })
169+ void forwardIfEqual_tooShort (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
170+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
171+ topicIterator .next ();
172+ final int start = topicIterator .getStart ();
173+ final int end = topicIterator .getEnd ();
174+
175+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
176+ assertFalse (topicIterator .forwardIfEqual (levels ));
177+
178+ assertEquals (start , topicIterator .getStart ());
179+ assertEquals (end , topicIterator .getEnd ());
180+ }
181+
182+ @ ParameterizedTest
183+ @ CsvSource ({
184+ "test, test/topic" , "test/topic/filter, test/topic/filte2" , "test/topic/filter, test/topic/filter2" ,
185+ "test/topic/filter2, test/topic/filter" , "test/+/topic, test/+/topi2" , "test/+/topic, test/+/topic2" ,
186+ "test/+/topic2, test/+/topic" , "test/+/topic/#, test/+/topi2" , "test/+/topic/#, test/+/topic2" ,
187+ "test/+/topic2/#, test/+/topic" , "test/topic/+, test/topic/abc"
188+ })
189+ void forwardIfEqual_notFullyEqual (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
190+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
191+ topicIterator .next ();
192+ final int start = topicIterator .getStart ();
193+ final int end = topicIterator .getEnd ();
194+
195+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
196+ assertFalse (topicIterator .forwardIfEqual (levels ));
197+
198+ assertEquals (start , topicIterator .getStart ());
199+ assertEquals (end , topicIterator .getEnd ());
200+ }
201+
202+ @ ParameterizedTest
203+ @ CsvSource ({"test/topic/filter, test/topic/filter" , "test/+/topic, test/+/topic" , "test/+/topic/#, test/+/topic" })
204+ void forwardWhileEqual_equalToEnd (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
205+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
206+ topicIterator .next ();
207+
208+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
209+ assertEquals (topicLevels .length (), topicIterator .forwardWhileEqual (levels ));
210+
211+ assertFalse (topicIterator .hasNext ());
212+ assertEquals (topicFilter .endsWith ("#" ), topicIterator .hasMultiLevelWildcard ());
213+ }
214+
215+ @ ParameterizedTest
216+ @ CsvSource ({
217+ "test/topic/filter/abc, test/topic/filter" , "test/+/topic/abc, test/+/topic" ,
218+ "test/+/topic/abc/#, test/+/topic"
219+ })
220+ void forwardWhileEqual_remainingLevels (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
221+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
222+ topicIterator .next ();
223+
224+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
225+ assertEquals (topicLevels .length (), topicIterator .forwardWhileEqual (levels ));
226+
227+ assertTrue (topicIterator .hasNext ());
228+ final MqttTopicLevel lastLevel = topicIterator .next ();
229+ assertEquals (MqttTopicLevel .of ("abc" .getBytes (), 0 , 3 ), lastLevel );
230+ assertFalse (lastLevel .isSingleLevelWildcard ());
231+ assertFalse (topicIterator .hasNext ());
232+ assertEquals (topicFilter .endsWith ("#" ), topicIterator .hasMultiLevelWildcard ());
233+ }
234+
235+ @ ParameterizedTest
236+ @ CsvSource ({
237+ "test, test/topic" , "test/topic/filter, test/topic/filter/abc" , "test/+/topic, test/+/topic/abc" ,
238+ "test/+/topic/#, test/+/topic/abc"
239+ })
240+ void forwardWhileEqual_tooShort (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
241+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
242+ topicIterator .next ();
243+
244+ final boolean hasMultiLevelWildcard = topicFilter .endsWith ("#" );
245+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
246+ assertEquals (
247+ hasMultiLevelWildcard ? topicFilter .length () - 2 : topicFilter .length (),
248+ topicIterator .forwardWhileEqual (levels ));
249+
250+ assertFalse (topicIterator .hasNext ());
251+ assertEquals (hasMultiLevelWildcard , topicIterator .hasMultiLevelWildcard ());
252+ }
253+
254+ @ ParameterizedTest
255+ @ CsvSource ({
256+ "test/topic/filter, test/topic/filte2, 10" , "test/topic/filter, test/topic/filter2, 10" ,
257+ "test/topic/filter2, test/topic/filter, 10" , "test/+/topic, test/+/topi2, 6" ,
258+ "test/+/topic, test/+/topic2, 6" , "test/+/topic2, test/+/topic, 6" , "test/+/topic/#, test/+/topi2, 6" ,
259+ "test/+/topic/#, test/+/topic2, 6" , "test/+/topic2/#, test/+/topic, 6" , "test/topic/+, test/topic/abc, 10"
260+ })
261+ void forwardWhileEqual_notFullyEqual (
262+ final @ NotNull String topicFilter , final @ NotNull String topicLevels , final int branchIndex ) {
263+
264+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
265+ topicIterator .next ();
266+
267+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
268+ assertEquals (branchIndex , topicIterator .forwardWhileEqual (levels ));
269+
270+ assertEquals (branchIndex , topicIterator .getStart ());
271+ assertEquals (branchIndex , topicIterator .getEnd ());
272+ }
273+
274+ @ ParameterizedTest
275+ @ CsvSource (
276+ {"test/topic/filter, test/topic/filter" , "test/topic/filter, test/+/filter" , "test/topic/filter, test/+/+" })
277+ void forwardIfMatch_equalToEnd (final @ NotNull String topic , final @ NotNull String topicLevels ) {
278+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicImpl .of (topic ));
279+ topicIterator .next ();
280+
281+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
282+ assertTrue (topicIterator .forwardIfMatch (levels ));
283+
284+ assertFalse (topicIterator .hasNext ());
285+ }
286+
287+ @ ParameterizedTest
288+ @ CsvSource ({
289+ "test/topic/filter/abc, test/topic/filter" , "test/topic/filter/abc, test/+/filter" ,
290+ "test/topic/filter/abc, test/+/+"
291+ })
292+ void forwardIfMatch_remainingLevels (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
293+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
294+ topicIterator .next ();
295+
296+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
297+ assertTrue (topicIterator .forwardIfMatch (levels ));
298+
299+ assertTrue (topicIterator .hasNext ());
300+ final MqttTopicLevel lastLevel = topicIterator .next ();
301+ assertEquals (MqttTopicLevel .of ("abc" .getBytes (), 0 , 3 ), lastLevel );
302+ assertFalse (lastLevel .isSingleLevelWildcard ());
303+ assertFalse (topicIterator .hasNext ());
304+ }
305+
306+ @ ParameterizedTest
307+ @ CsvSource ({
308+ "test/topic/filter, test/topic/filter/abc" , "test/topic/filter, test/+/filter/abc" ,
309+ "test/topic/filter, test/+/+/abc" , "test/topic/filter, test/+/filter/+"
310+ })
311+ void forwardIfMatch_tooShort (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
312+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
313+ topicIterator .next ();
314+ final int start = topicIterator .getStart ();
315+ final int end = topicIterator .getEnd ();
316+
317+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
318+ assertFalse (topicIterator .forwardIfMatch (levels ));
319+
320+ assertEquals (start , topicIterator .getStart ());
321+ assertEquals (end , topicIterator .getEnd ());
322+ }
323+
324+ @ ParameterizedTest
325+ @ CsvSource ({
326+ "test, test/topic" , "test/topic/filter, test/topic/filte2" , "test/topic/filter, test/topic/filter2" ,
327+ "test/topic/filter2, test/topic/filter" , "test/topic/filter, test/+/filte2" ,
328+ "test/topic/filter, test/+/filter2" , "test/topic/filter2, test/+/filter"
329+ })
330+ void forwardIfMatch_notFullyEqual (final @ NotNull String topicFilter , final @ NotNull String topicLevels ) {
331+ final MqttTopicIterator topicIterator = MqttTopicIterator .of (MqttTopicFilterImpl .of (topicFilter ));
332+ topicIterator .next ();
333+ final int start = topicIterator .getStart ();
334+ final int end = topicIterator .getEnd ();
335+
336+ final MqttTopicLevels levels = createTopicLevels (topicLevels );
337+ assertFalse (topicIterator .forwardIfMatch (levels ));
338+
339+ assertEquals (start , topicIterator .getStart ());
340+ assertEquals (end , topicIterator .getEnd ());
341+ }
342+
343+ private static @ NotNull MqttTopicLevels createTopicLevels (final @ NotNull String levels ) {
344+ final byte [] bytes = levels .getBytes ();
345+ final int firstEnd = levels .indexOf ('/' );
346+ return new MqttTopicLevels (bytes , (firstEnd == -1 ) ? bytes .length : firstEnd );
347+ }
348+ }
0 commit comments