Skip to content

Commit 292e2d9

Browse files
committed
add tests for basic connector functionality
Added tests for connector API. Fixed involuntary re-connects issue. Closes #51
1 parent a96c8d9 commit 292e2d9

13 files changed

+1216
-8
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ before_script:
1212
- src/test/travis.pre.sh
1313

1414
script:
15-
- mvn test
15+
- mvn verify
1616
- sudo cat /var/log/tarantool/jdk-testing.log

pom.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@
5050
<artifactId>maven-surefire-plugin</artifactId>
5151
<version>2.22.0</version>
5252
</plugin>
53+
<plugin>
54+
<groupId>org.apache.maven.plugins</groupId>
55+
<artifactId>maven-failsafe-plugin</artifactId>
56+
<version>2.22.0</version>
57+
<executions>
58+
<execution>
59+
<goals>
60+
<goal>integration-test</goal>
61+
<goal>verify</goal>
62+
</goals>
63+
</execution>
64+
</executions>
65+
</plugin>
5366
</plugins>
5467
</build>
5568

@@ -66,6 +79,12 @@
6679
<version>1.9.5</version>
6780
<scope>test</scope>
6881
</dependency>
82+
<dependency>
83+
<groupId>org.yaml</groupId>
84+
<artifactId>snakeyaml</artifactId>
85+
<version>1.23</version>
86+
<scope>test</scope>
87+
</dependency>
6988
</dependencies>
7089

7190
<parent>

src/main/java/org/tarantool/TarantoolClientImpl.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ public class TarantoolClientImpl extends TarantoolBase<Future<List<?>>> implemen
6363
@Override
6464
public void run() {
6565
while (!Thread.currentThread().isInterrupted()) {
66-
reconnect(0, thumbstone);
66+
// Since LockSupport.park() can return at any time for no reason,
67+
// we need to check if conditions are right to re-connect.
68+
if (thumbstone != null) {
69+
reconnect(0, thumbstone);
70+
}
6771
LockSupport.park();
6872
}
6973
}
@@ -103,7 +107,8 @@ public TarantoolClientImpl(SocketChannelProvider socketProvider, TarantoolClient
103107

104108
protected void reconnect(int retry, Throwable lastError) {
105109
SocketChannel channel;
106-
while (!Thread.interrupted()) {
110+
// Thread.interrupted() clears the flag, prefer isInterrupted().
111+
while (!Thread.currentThread().isInterrupted()) {
107112
channel = socketProvider.get(retry++, lastError == NOT_INIT_EXCEPTION ? null : lastError);
108113
try {
109114
connect(channel);
@@ -503,7 +508,7 @@ public List exec(Code code, Object... args) {
503508

504509
@Override
505510
public void close() {
506-
throw new IllegalStateException("You should close TarantoolClient to make this");
511+
throw new IllegalStateException("You should close TarantoolClient instead.");
507512
}
508513
}
509514

@@ -525,7 +530,7 @@ public Long exec(Code code, Object... args) {
525530

526531
@Override
527532
public void close() {
528-
throw new IllegalStateException("You should close TarantoolClient to make this");
533+
throw new IllegalStateException("You should close TarantoolClient instead.");
529534
}
530535
}
531536

src/test/instance.lua

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ box.once('init', function()
1616
box.schema.user.create('test_ordin', { password = '2HWRXHfa' })
1717
box.schema.user.create('test_admin', { password = '4pWBZmLEgkmKK5WP' })
1818

19-
box.schema.user.grant('test_ordin', 'read,write', 'user')
20-
box.schema.user.grant('test_admin', 'execute', 'super')
19+
box.schema.user.grant('test_ordin', 'read,write', 'space', 'user')
20+
box.schema.user.grant('test_admin', 'super')
2121
end)
2222

23+
-- Java has no internal support for unix domain sockets,
24+
-- so we will use tcp for console communication.
25+
console = require('console')
26+
console.listen(3313)
27+
console.start()
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
package org.tarantool;
2+
3+
import org.junit.jupiter.api.AfterAll;
4+
import org.junit.jupiter.api.BeforeAll;
5+
6+
import java.io.BufferedReader;
7+
import java.io.IOException;
8+
import java.io.InputStreamReader;
9+
import java.io.OutputStreamWriter;
10+
import java.net.InetSocketAddress;
11+
import java.net.Socket;
12+
import java.nio.channels.SocketChannel;
13+
import java.util.List;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
import static org.junit.jupiter.api.Assertions.assertNotNull;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
19+
/**
20+
* Abstract test. Provides environment control and frequently used functions.
21+
*/
22+
public abstract class AbstractTarantoolConnectorIT {
23+
protected static final String host = System.getProperty("tntHost", "localhost");
24+
protected static final int port = Integer.parseInt(System.getProperty("tntPort", "3301"));
25+
protected static final int consolePort = Integer.parseInt(System.getProperty("tntConsolePort", "3313"));
26+
protected static final int tntCtlPort = Integer.parseInt(System.getProperty("tntCtlPort", "3000"));
27+
protected static final String username = System.getProperty("tntUser", "test_admin");
28+
protected static final String password = System.getProperty("tntPass", "4pWBZmLEgkmKK5WP");
29+
30+
protected static final SocketChannelProvider socketChannelProvider = new TestSocketChannelProvider(host, port);
31+
32+
protected static final int TIMEOUT = 500;
33+
protected static final int RESTART_TIMEOUT = 2000;
34+
35+
protected static TarantoolConsole console;
36+
37+
protected static final String SPACE_NAME = "basic_test";
38+
protected static final String MULTIPART_SPACE_NAME = "multipart_test";
39+
40+
protected static int SPACE_ID;
41+
protected static int MULTI_PART_SPACE_ID;
42+
43+
protected static int PK_INDEX_ID;
44+
protected static int MPK_INDEX_ID;
45+
protected static int VIDX_INDEX_ID;
46+
47+
private static final String[] setupScript = new String[] {
48+
"box.schema.space.create('basic_test', { format = " +
49+
"{{name = 'id', type = 'integer'}," +
50+
" {name = 'val', type = 'string'} } })",
51+
52+
"box.space.basic_test:create_index('pk', { type = 'TREE', parts = {'id'} } )",
53+
"box.space.basic_test:create_index('vidx', { type = 'TREE', unique = false, parts = {'val'} } )",
54+
55+
"box.space.basic_test:replace{1, 'one'}",
56+
"box.space.basic_test:replace{2, 'two'}",
57+
"box.space.basic_test:replace{3, 'three'}",
58+
59+
"box.schema.space.create('multipart_test', { format = " +
60+
"{{name = 'id1', type = 'integer'}," +
61+
" {name = 'id2', type = 'string'}," +
62+
" {name = 'val1', type = 'string'} } })",
63+
64+
"box.space.multipart_test:create_index('pk', { type = 'TREE', parts = {'id1', 'id2'} })",
65+
"box.space.multipart_test:create_index('vidx', { type = 'TREE', unique = false, parts = {'val1'} })",
66+
67+
"box.space.multipart_test:replace{1, 'one', 'o n e'}",
68+
"box.space.multipart_test:replace{2, 'two', 't w o'}",
69+
"box.space.multipart_test:replace{3, 'three', 't h r e e'}",
70+
71+
"function echo(...) return ... end"
72+
};
73+
74+
private static final String[] cleanScript = new String[] {
75+
"box.space.basic_test and box.space.basic_test:drop()",
76+
"box.space.multipart_test and box.space.multipart_test:drop()"
77+
};
78+
79+
@BeforeAll
80+
public static void setupEnv() {
81+
executeLua(cleanScript);
82+
executeLua(setupScript);
83+
84+
console = getConsole();
85+
86+
SPACE_ID = console.eval("box.space.basic_test.id");
87+
PK_INDEX_ID = console.eval("box.space.basic_test.index.pk.id");
88+
VIDX_INDEX_ID = console.eval("box.space.basic_test.index.vidx.id");
89+
90+
MULTI_PART_SPACE_ID = console.eval("box.space.multipart_test.id");
91+
MPK_INDEX_ID = console.eval("box.space.multipart_test.index.pk.id");
92+
}
93+
94+
@AfterAll
95+
public static void cleanupEnv() {
96+
console.close();
97+
98+
executeLua(cleanScript);
99+
}
100+
101+
private static void executeLua(String[] exprs) {
102+
TarantoolConsole conn = getConsole();
103+
try {
104+
for (String expr : exprs)
105+
conn.exec(expr);
106+
} finally {
107+
conn.close();
108+
}
109+
}
110+
111+
protected void checkTupleResult(Object res, List tuple) {
112+
assertNotNull(res);
113+
assertTrue(List.class.isAssignableFrom(res.getClass()));
114+
List list = (List)res;
115+
assertEquals(1, list.size());
116+
assertNotNull(list.get(0));
117+
assertTrue(List.class.isAssignableFrom(list.get(0).getClass()));
118+
assertEquals(tuple, list.get(0));
119+
}
120+
121+
protected TarantoolClient getClient() {
122+
TarantoolClientConfig config = new TarantoolClientConfig();
123+
config.username = username;
124+
config.password = password;
125+
config.initTimeoutMillis = 1000;
126+
config.sharedBufferSize = 128;
127+
128+
return new TarantoolClientImpl(socketChannelProvider, config);
129+
}
130+
131+
protected static TarantoolConsole getConsole() {
132+
return TarantoolConsole.open(host, consolePort);
133+
}
134+
135+
protected static TarantoolConsole getConsole(String instance) {
136+
return TarantoolConsole.open(host, tntCtlPort, instance);
137+
}
138+
139+
protected TarantoolConnection getConnection() {
140+
Socket socket = new Socket();
141+
try {
142+
socket.connect(new InetSocketAddress(host, port));
143+
} catch (IOException e) {
144+
throw new RuntimeException("Test failed due to invalid environment.", e);
145+
}
146+
try {
147+
return new TarantoolConnection(username, password, socket);
148+
} catch (Exception e) {
149+
try {
150+
socket.close();
151+
} catch (IOException ignored) {
152+
// No-op.
153+
}
154+
throw new RuntimeException(e);
155+
}
156+
}
157+
158+
protected List<?> consoleSelect(String spaceName, Object key) {
159+
StringBuilder sb = new StringBuilder("box.space.");
160+
sb.append(spaceName);
161+
sb.append(":select{");
162+
if (List.class.isAssignableFrom(key.getClass())) {
163+
List parts = (List)key;
164+
for (int i = 0; i < parts.size(); i++) {
165+
if (i != 0)
166+
sb.append(", ");
167+
Object k = parts.get(i);
168+
if (k.getClass().isAssignableFrom(String.class)) {
169+
sb.append('\'');
170+
sb.append(k);
171+
sb.append('\'');
172+
} else {
173+
sb.append(k);
174+
}
175+
}
176+
} else {
177+
sb.append(key);
178+
}
179+
sb.append("}");
180+
return console.eval(sb.toString());
181+
}
182+
183+
protected void stopTarantool(String instance) {
184+
tarantoolControl("stop", instance);
185+
}
186+
187+
protected void startTarantool(String instance) {
188+
tarantoolControl("start", instance);
189+
}
190+
191+
/**
192+
* Control the given tarantool instance via tarantoolctl utility network endpoint.
193+
*
194+
* @param command A tarantoolctl utility command.
195+
* @param instanceName Name of tarantool instance to control.
196+
*/
197+
private void tarantoolControl(String command, String instanceName) {
198+
int timeout = RESTART_TIMEOUT;
199+
Socket socket = new Socket();
200+
try {
201+
socket.connect(new InetSocketAddress(host, tntCtlPort), timeout);
202+
socket.setSoTimeout(timeout);
203+
OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
204+
writer.write(command + " " + instanceName + "\n");
205+
writer.flush();
206+
BufferedReader rdr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
207+
String line;
208+
while ((line = rdr.readLine()) != null)
209+
System.out.println(line);
210+
} catch (Exception e) {
211+
throw new RuntimeException("Test failed due to environment control failure.", e);
212+
} finally {
213+
try {
214+
socket.close();
215+
} catch (Exception e) {
216+
// No-op.
217+
}
218+
}
219+
}
220+
221+
/**
222+
* Socket channel provider to be used throughout the tests.
223+
*/
224+
public static class TestSocketChannelProvider implements SocketChannelProvider {
225+
String host;
226+
int port;
227+
228+
public TestSocketChannelProvider(String host, int port) {
229+
this.host = host;
230+
this.port = port;
231+
}
232+
233+
@Override
234+
public SocketChannel get(int retryNumber, Throwable lastError) {
235+
long budget = System.currentTimeMillis() + RESTART_TIMEOUT;
236+
while (!Thread.currentThread().isInterrupted()) {
237+
try {
238+
return SocketChannel.open(new InetSocketAddress(host, port));
239+
} catch (Exception e) {
240+
if (budget < System.currentTimeMillis())
241+
throw new RuntimeException(e);
242+
try {
243+
Thread.sleep(100);
244+
} catch (InterruptedException ex) {
245+
// No-op.
246+
Thread.currentThread().interrupt();
247+
}
248+
}
249+
}
250+
throw new RuntimeException("Test failure due to invalid environment. " +
251+
"Timeout connecting to " + host + ":" + port);
252+
}
253+
}
254+
}

0 commit comments

Comments
 (0)