Skip to content

Commit 6a69e5d

Browse files
author
dxcity
committed
tagging release 3.284
1 parent 4990fcc commit 6a69e5d

File tree

53 files changed

+484
-337
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+484
-337
lines changed

ReleaseNotes.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11

2+
QDS 3.284:
3+
4+
* [QD-1109] Sometimes getTimeSeriesPromise returns empty list
5+
26
QDS 3.283:
37

48
* [QD-1219] Add logging of JMX console initialization errors

auth/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<artifactId>QD</artifactId>
1616
<groupId>com.devexperts.qd</groupId>
17-
<version>3.283</version>
17+
<version>3.284</version>
1818
<relativePath>../pom.xml</relativePath>
1919
</parent>
2020
<modelVersion>4.0.0</modelVersion>

dxfeed-api/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<artifactId>QD</artifactId>
1616
<groupId>com.devexperts.qd</groupId>
17-
<version>3.283</version>
17+
<version>3.284</version>
1818
<relativePath>../pom.xml</relativePath>
1919
</parent>
2020
<modelVersion>4.0.0</modelVersion>

dxfeed-bin/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<artifactId>QD</artifactId>
1616
<groupId>com.devexperts.qd</groupId>
17-
<version>3.283</version>
17+
<version>3.284</version>
1818
<relativePath>../pom.xml</relativePath>
1919
</parent>
2020
<modelVersion>4.0.0</modelVersion>

dxfeed-codegen-verify/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<artifactId>QD</artifactId>
1616
<groupId>com.devexperts.qd</groupId>
17-
<version>3.283</version>
17+
<version>3.284</version>
1818
</parent>
1919
<modelVersion>4.0.0</modelVersion>
2020

dxfeed-codegen/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<artifactId>QD</artifactId>
1616
<groupId>com.devexperts.qd</groupId>
17-
<version>3.283</version>
17+
<version>3.284</version>
1818
<relativePath>../pom.xml</relativePath>
1919
</parent>
2020
<modelVersion>4.0.0</modelVersion>

dxfeed-impl/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<artifactId>QD</artifactId>
1616
<groupId>com.devexperts.qd</groupId>
17-
<version>3.283</version>
17+
<version>3.284</version>
1818
</parent>
1919
<modelVersion>4.0.0</modelVersion>
2020

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
/*
2+
* !++
3+
* QDS - Quick Data Signalling Library
4+
* !-
5+
* Copyright (C) 2002 - 2020 Devexperts LLC
6+
* !-
7+
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
8+
* If a copy of the MPL was not distributed with this file, You can obtain one at
9+
* http://mozilla.org/MPL/2.0/.
10+
* !__
11+
*/
12+
package com.dxfeed.api.test;
13+
14+
import com.devexperts.test.ThreadCleanCheck;
15+
import com.devexperts.util.TimeFormat;
16+
import com.dxfeed.api.DXEndpoint;
17+
import com.dxfeed.api.DXFeed;
18+
import com.dxfeed.api.DXPublisher;
19+
import com.dxfeed.api.osub.ObservableSubscriptionChangeListener;
20+
import com.dxfeed.api.osub.TimeSeriesSubscriptionSymbol;
21+
import com.dxfeed.event.IndexedEvent;
22+
import com.dxfeed.event.IndexedEventSource;
23+
import com.dxfeed.event.candle.Candle;
24+
import com.dxfeed.event.candle.CandlePeriod;
25+
import com.dxfeed.event.candle.CandleSymbol;
26+
import com.dxfeed.event.candle.CandleType;
27+
import com.dxfeed.event.market.Trade;
28+
import com.dxfeed.event.option.Series;
29+
import com.dxfeed.promise.Promise;
30+
import junit.framework.TestCase;
31+
32+
import java.util.Collections;
33+
import java.util.List;
34+
import java.util.Set;
35+
import java.util.concurrent.BlockingQueue;
36+
import java.util.concurrent.LinkedBlockingQueue;
37+
38+
public class DXFeedPromiseTest extends TestCase {
39+
private DXEndpoint endpoint;
40+
private DXFeed feed;
41+
private DXPublisher publisher;
42+
43+
private final BlockingQueue<Object> added = new LinkedBlockingQueue<>();
44+
private final BlockingQueue<Object> removed = new LinkedBlockingQueue<>();
45+
46+
@Override
47+
protected void setUp() throws Exception {
48+
ThreadCleanCheck.before();
49+
endpoint = DXEndpoint.create(DXEndpoint.Role.LOCAL_HUB);
50+
endpoint.executor(Runnable::run);
51+
feed = endpoint.getFeed();
52+
publisher = endpoint.getPublisher();
53+
}
54+
55+
@Override
56+
protected void tearDown() throws Exception {
57+
endpoint.close();
58+
ThreadCleanCheck.after();
59+
}
60+
61+
// ----- LastEventPromise -----
62+
63+
public void testLastEventPromise() {
64+
trackSubscription(Trade.class);
65+
66+
Promise<Trade> aPromise = feed.getLastEventPromise(Trade.class, "A");
67+
assertAdded("A");
68+
Promise<Trade> bPromise = feed.getLastEventPromise(Trade.class, "B");
69+
assertAdded("B");
70+
Promise<Trade> cPromise = feed.getLastEventPromise(Trade.class, "C");
71+
assertAdded("C");
72+
Promise<Trade> bPromise2 = feed.getLastEventPromise(Trade.class, "B");
73+
assertEmptyAddedAndRemoved();
74+
75+
assertFalse(aPromise.isDone());
76+
assertFalse(bPromise.isDone());
77+
assertFalse(cPromise.isDone());
78+
assertFalse(bPromise2.isDone());
79+
80+
publisher.publishEvents(Collections.singletonList(new Trade("A")));
81+
assertRemoved("A");
82+
assertTrue(aPromise.isDone());
83+
assertEquals("A", aPromise.getResult().getEventSymbol());
84+
assertFalse(bPromise.isDone());
85+
assertFalse(cPromise.isDone());
86+
assertFalse(bPromise2.isDone());
87+
88+
publisher.publishEvents(Collections.singletonList(new Trade("B")));
89+
assertRemoved("B");
90+
assertTrue(bPromise.isDone());
91+
assertEquals("B", bPromise.getResult().getEventSymbol());
92+
assertTrue(bPromise2.isDone());
93+
assertEquals("B", bPromise2.getResult().getEventSymbol());
94+
assertFalse(cPromise.isDone());
95+
96+
publisher.publishEvents(Collections.singletonList(new Trade("C")));
97+
assertRemoved("C");
98+
assertTrue(cPromise.isDone());
99+
assertEquals("C", cPromise.getResult().getEventSymbol());
100+
}
101+
102+
public void testLastEventPromiseCancel() {
103+
trackSubscription(Trade.class);
104+
105+
Promise<Trade> promise = feed.getLastEventPromise(Trade.class, "T");
106+
assertAdded("T");
107+
assertFalse(promise.isDone());
108+
109+
promise.cancel();
110+
assertRemoved("T");
111+
assertTrue(promise.isDone());
112+
}
113+
114+
// ----- IndexedEventsPromise -----
115+
116+
private static final String SERIES_SYMBOL = "TEST";
117+
118+
public void testIndexedEventsPromise() {
119+
// Series is a simple indexed event with plain delegate logic (unlike Order)
120+
trackSubscription(Series.class);
121+
122+
Promise<List<Series>> promise =
123+
feed.getIndexedEventsPromise(Series.class, SERIES_SYMBOL, IndexedEventSource.DEFAULT);
124+
assertAdded(SERIES_SYMBOL);
125+
assertFalse(promise.isDone());
126+
127+
publishSeries(3, 300, 10.01, IndexedEvent.SNAPSHOT_BEGIN);
128+
assertFalse(promise.isDone());
129+
publishSeries(2, 200, 10.02, 0);
130+
assertFalse(promise.isDone());
131+
publishSeries(1, 100, 10.03, 0);
132+
assertFalse(promise.isDone());
133+
publishSeries(0, 0, Double.NaN, IndexedEvent.SNAPSHOT_END | IndexedEvent.REMOVE_EVENT);
134+
assertRemoved(SERIES_SYMBOL);
135+
assertTrue(promise.isDone());
136+
List<Series> list = promise.getResult();
137+
assertEquals(3, list.size());
138+
assertSeries(list.get(0), 1, 100, 10.03);
139+
assertSeries(list.get(1), 2, 200, 10.02);
140+
assertSeries(list.get(2), 3, 300, 10.01);
141+
}
142+
143+
private void publishSeries(long index, int expiration, double volatility, int eventFlags) {
144+
Series series = new Series(SERIES_SYMBOL);
145+
series.setIndex(index);
146+
series.setExpiration(expiration);
147+
series.setVolatility(volatility);
148+
series.setEventFlags(eventFlags);
149+
publisher.publishEvents(Collections.singletonList(series));
150+
}
151+
152+
private void assertSeries(Series series, long index, int expiration, double volatility) {
153+
assertEquals(SERIES_SYMBOL, series.getEventSymbol());
154+
assertEquals(index, series.getIndex());
155+
assertEquals(expiration, series.getExpiration());
156+
assertEquals(volatility, series.getVolatility());
157+
assertEquals(0, series.getEventFlags());
158+
}
159+
160+
// ----- TimeSeriesPromise -----
161+
162+
private static final CandleSymbol CANDLE_SYMBOL =
163+
CandleSymbol.valueOf("TEST", CandlePeriod.valueOf(1, CandleType.MINUTE));
164+
165+
public void testTimeSeriesPromise() {
166+
trackSubscription(Candle.class);
167+
long time = TimeFormat.DEFAULT.parse("20200116-120000-0500").getTime();
168+
long period = 60_000;
169+
long subTime = time - 2 * period;
170+
171+
Promise<List<Candle>> promise = feed.getTimeSeriesPromise(Candle.class, CANDLE_SYMBOL, subTime, time);
172+
TimeSeriesSubscriptionSymbol addedSymbol = (TimeSeriesSubscriptionSymbol) added.poll();
173+
assertNotNull(addedSymbol);
174+
assertEquals(CANDLE_SYMBOL, addedSymbol.getEventSymbol());
175+
assertTrue(addedSymbol.getFromTime() <= subTime);
176+
assertEmptyAddedAndRemoved();
177+
assertFalse(promise.isDone());
178+
179+
publishCandle(time, 0, IndexedEvent.SNAPSHOT_BEGIN);
180+
assertFalse(promise.isDone());
181+
publishCandle(time - period, 1, 0);
182+
assertFalse(promise.isDone());
183+
publishCandle(subTime, 2, 0);
184+
assertRemoved(addedSymbol);
185+
assertTrue(promise.isDone());
186+
List<Candle> list = promise.getResult();
187+
assertEquals(3, list.size());
188+
assertCandle(list.get(0), subTime, 2);
189+
assertCandle(list.get(1), time - period, 1);
190+
assertCandle(list.get(2), time, 0);
191+
}
192+
193+
// Test case for [QD-1109] Sometimes getTimeSeriesPromise returns empty list
194+
public void testEmptyTimeSeriesPromise() {
195+
// Candle is an intricate time series event with complex fetch time heuristic logic
196+
trackSubscription(Candle.class);
197+
long time = TimeFormat.DEFAULT.parse("20200116-120000-0500").getTime();
198+
long period = 60_000;
199+
long subTime = time - 2 * period;
200+
201+
Promise<List<Candle>> promise = feed.getTimeSeriesPromise(Candle.class, CANDLE_SYMBOL, subTime, time);
202+
TimeSeriesSubscriptionSymbol addedSymbol = (TimeSeriesSubscriptionSymbol) added.poll();
203+
assertNotNull(addedSymbol);
204+
assertEquals(CANDLE_SYMBOL, addedSymbol.getEventSymbol());
205+
assertTrue(addedSymbol.getFromTime() <= subTime);
206+
assertEmptyAddedAndRemoved();
207+
assertFalse(promise.isDone());
208+
209+
publishCandle(time, 0, IndexedEvent.SNAPSHOT_BEGIN);
210+
assertFalse(promise.isDone());
211+
publishCandle(time - period, 1, 0);
212+
assertFalse(promise.isDone());
213+
publishCandle(subTime, 2, 0);
214+
assertRemoved(addedSymbol);
215+
assertTrue(promise.isDone());
216+
List<Candle> list = promise.getResult();
217+
assertEquals(3, list.size());
218+
assertCandle(list.get(0), subTime, 2);
219+
assertCandle(list.get(1), time - period, 1);
220+
assertCandle(list.get(2), time, 0);
221+
222+
// The DXFeed implementation actually subscribes to some fromTime which is lower than subTime.
223+
// Data provider sends data up to subscribed fromTime. Sometimes the entire block of data is split.
224+
// When split interleaves with creation of second promise - that promise is completed with no data.
225+
// This is the case behind [QD-1109] reproduced here with artificial split interleaved with second promise.
226+
227+
Promise<List<Candle>> promise2 = feed.getTimeSeriesPromise(Candle.class, CANDLE_SYMBOL, subTime, time);
228+
TimeSeriesSubscriptionSymbol addedSymbol2 = (TimeSeriesSubscriptionSymbol) added.poll();
229+
assertNotNull(addedSymbol2);
230+
assertEquals(CANDLE_SYMBOL, addedSymbol2.getEventSymbol());
231+
assertEquals(addedSymbol.getFromTime(), addedSymbol2.getFromTime());
232+
assertEmptyAddedAndRemoved();
233+
assertFalse(promise2.isDone());
234+
235+
// Pretend to continue sending of data after split
236+
publishCandle(time - 3 * period, 3, 0);
237+
assertFalse(promise2.isDone());
238+
239+
// Pretend to catch some part of full answer but without proper SNAPSHOT_BEGIN flag
240+
publishCandle(time - period, 1, 0);
241+
publishCandle(subTime, 2, 0);
242+
publishCandle(time - 3 * period, 3, 0);
243+
assertFalse(promise2.isDone());
244+
245+
// Send proper snapshot from scratch
246+
publishCandle(time, 0, IndexedEvent.SNAPSHOT_BEGIN);
247+
assertFalse(promise2.isDone());
248+
publishCandle(time - period, 1, 0);
249+
assertFalse(promise2.isDone());
250+
publishCandle(subTime, 2, 0);
251+
assertRemoved(addedSymbol);
252+
assertTrue(promise2.isDone());
253+
list = promise2.getResult();
254+
assertEquals(3, list.size());
255+
assertCandle(list.get(0), subTime, 2);
256+
assertCandle(list.get(1), time - period, 1);
257+
assertCandle(list.get(2), time, 0);
258+
}
259+
260+
private void publishCandle(long time, long count, int eventFlags) {
261+
Candle candle = new Candle(CANDLE_SYMBOL);
262+
candle.setTime(time);
263+
candle.setCount(count);
264+
candle.setEventFlags(eventFlags);
265+
publisher.publishEvents(Collections.singletonList(candle));
266+
}
267+
268+
private void assertCandle(Candle candle, long time, long count) {
269+
assertEquals(CANDLE_SYMBOL, candle.getEventSymbol());
270+
assertEquals(time, candle.getTime());
271+
assertEquals(count, candle.getCount());
272+
assertEquals(0, candle.getEventFlags());
273+
}
274+
275+
// ========== shared test utility ==========
276+
277+
private void trackSubscription(Class<?> eventType) {
278+
assertEmptyAddedAndRemoved();
279+
publisher.getSubscription(eventType).addChangeListener(new ObservableSubscriptionChangeListener() {
280+
@Override
281+
public void symbolsAdded(Set<?> symbols) {
282+
added.addAll(symbols);
283+
}
284+
285+
@Override
286+
public void symbolsRemoved(Set<?> symbols) {
287+
removed.addAll(symbols);
288+
}
289+
});
290+
}
291+
292+
private void assertAdded(Object symbol) {
293+
assertEquals(symbol, added.poll());
294+
assertEmptyAddedAndRemoved();
295+
}
296+
297+
private void assertRemoved(Object symbol) {
298+
assertEquals(symbol, removed.poll());
299+
assertEmptyAddedAndRemoved();
300+
}
301+
302+
private void assertEmptyAddedAndRemoved() {
303+
assertEquals(0, added.size());
304+
assertEquals(0, removed.size());
305+
}
306+
}

0 commit comments

Comments
 (0)