From c0f3d120f8c49490d84f4992ba492197571c8975 Mon Sep 17 00:00:00 2001 From: Sergei Kalashnikov Date: Mon, 19 Nov 2018 20:03:45 +0300 Subject: [PATCH] add tests for basic connector functionality Added tests for connector API. Closes #51 --- .gitignore | 1 + .travis.yml | 4 +- README.md | 31 ++ pom.xml | 19 + .../org/tarantool/TarantoolClientImpl.java | 4 +- src/test/.tarantoolctl | 15 + .../AbstractTarantoolConnectorIT.java | 187 +++++++++ .../org/tarantool/AbstractTarantoolOpsIT.java | 371 ++++++++++++++++++ .../tarantool/AsyncClientOperationsIT.java | 119 ++++++ .../org/tarantool/ClientOperationsIT.java | 46 +++ .../java/org/tarantool/ClientReconnectIT.java | 59 +++ src/test/java/org/tarantool/ConnectionIT.java | 36 ++ .../FireAndForgetClientOperationsIT.java | 83 ++++ .../java/org/tarantool/TarantoolConsole.java | 241 ++++++++++++ .../java/org/tarantool/TarantoolControl.java | 173 ++++++++ .../tarantool/TestSocketChannelProvider.java | 40 ++ src/test/{instance.lua => jdk-testing.lua} | 12 +- src/test/travis.pre.sh | 6 +- 18 files changed, 1434 insertions(+), 13 deletions(-) create mode 100644 src/test/.tarantoolctl create mode 100644 src/test/java/org/tarantool/AbstractTarantoolConnectorIT.java create mode 100644 src/test/java/org/tarantool/AbstractTarantoolOpsIT.java create mode 100644 src/test/java/org/tarantool/AsyncClientOperationsIT.java create mode 100644 src/test/java/org/tarantool/ClientOperationsIT.java create mode 100644 src/test/java/org/tarantool/ClientReconnectIT.java create mode 100644 src/test/java/org/tarantool/ConnectionIT.java create mode 100644 src/test/java/org/tarantool/FireAndForgetClientOperationsIT.java create mode 100644 src/test/java/org/tarantool/TarantoolConsole.java create mode 100644 src/test/java/org/tarantool/TarantoolControl.java create mode 100644 src/test/java/org/tarantool/TestSocketChannelProvider.java rename src/test/{instance.lua => jdk-testing.lua} (63%) diff --git a/.gitignore b/.gitignore index ff1ababa..2e7b975c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ out target build +testroot diff --git a/.travis.yml b/.travis.yml index e834c3e9..eefedf82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,5 @@ before_script: - src/test/travis.pre.sh script: - - mvn test - - sudo cat /var/log/tarantool/jdk-testing.log + - mvn verify + - cat testroot/jdk-testing.log diff --git a/README.md b/README.md index c41fc8db..f1ea60f3 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,37 @@ Remember that `TarantoolClient` adopts a [fail-fast](https://en.wikipedia.org/wiki/Fail-fast) policy when a client is not connected. +The `TarantoolClient` will stop functioning if your implementation of a socket +channel provider raises an exception or returns a null. You will need a new +instance of client to recover. Hence, you should only throw in case you have +met unrecoverable error. + +Below is an example of `SocketChannelProvider` implementation that handles short +tarantool restarts. + +```java +SocketChannelProvider socketChannelProvider = new SocketChannelProvider() { + @Override + public SocketChannel get(int retryNumber, Throwable lastError) { + long deadline = System.currentTimeMillis() + RESTART_TIMEOUT; + while (!Thread.currentThread().isInterrupted()) { + try { + return SocketChannel.open(new InetSocketAddress("localhost", 3301)); + } catch (IOException e) { + if (deadline < System.currentTimeMillis()) + throw new RuntimeException(e); + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } + } + throw new RuntimeException(new TimeoutException("Connect timed out.")); + } +}; +``` + 4. Create a client. ```java diff --git a/pom.xml b/pom.xml index da426ce6..e66df25c 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,19 @@ maven-surefire-plugin 2.22.0 + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.0 + + + + integration-test + verify + + + + @@ -66,6 +79,12 @@ 1.9.5 test + + org.yaml + snakeyaml + 1.23 + test + diff --git a/src/main/java/org/tarantool/TarantoolClientImpl.java b/src/main/java/org/tarantool/TarantoolClientImpl.java index e2567ae5..67c176c7 100644 --- a/src/main/java/org/tarantool/TarantoolClientImpl.java +++ b/src/main/java/org/tarantool/TarantoolClientImpl.java @@ -503,7 +503,7 @@ public List exec(Code code, Object... args) { @Override public void close() { - throw new IllegalStateException("You should close TarantoolClient to make this"); + throw new IllegalStateException("You should close TarantoolClient instead."); } } @@ -525,7 +525,7 @@ public Long exec(Code code, Object... args) { @Override public void close() { - throw new IllegalStateException("You should close TarantoolClient to make this"); + throw new IllegalStateException("You should close TarantoolClient instead."); } } diff --git a/src/test/.tarantoolctl b/src/test/.tarantoolctl new file mode 100644 index 00000000..3dea4514 --- /dev/null +++ b/src/test/.tarantoolctl @@ -0,0 +1,15 @@ +-- Options for tarantoolctl. + +local workdir = os.getenv('TEST_WORKDIR') +default_cfg = { + pid_file = workdir, + wal_dir = workdir, + memtx_dir = workdir, + vinyl_dir = workdir, + log = workdir, + background = true, +} + +instance_dir = workdir + +-- vim: set ft=lua : diff --git a/src/test/java/org/tarantool/AbstractTarantoolConnectorIT.java b/src/test/java/org/tarantool/AbstractTarantoolConnectorIT.java new file mode 100644 index 00000000..38772915 --- /dev/null +++ b/src/test/java/org/tarantool/AbstractTarantoolConnectorIT.java @@ -0,0 +1,187 @@ +package org.tarantool; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Abstract test. Provides environment control and frequently used functions. + */ +public abstract class AbstractTarantoolConnectorIT { + protected static final String host = System.getProperty("tntHost", "localhost"); + protected static final int port = Integer.parseInt(System.getProperty("tntPort", "3301")); + protected static final int consolePort = Integer.parseInt(System.getProperty("tntConsolePort", "3313")); + protected static final String username = System.getProperty("tntUser", "test_admin"); + protected static final String password = System.getProperty("tntPass", "4pWBZmLEgkmKK5WP"); + + protected static final int TIMEOUT = 500; + protected static final int RESTART_TIMEOUT = 2000; + + protected static final SocketChannelProvider socketChannelProvider = new TestSocketChannelProvider(host, port, + RESTART_TIMEOUT); + + protected static TarantoolControl control; + protected static TarantoolConsole console; + + protected static final String SPACE_NAME = "basic_test"; + protected static final String MULTIPART_SPACE_NAME = "multipart_test"; + + protected static int SPACE_ID; + protected static int MULTI_PART_SPACE_ID; + + protected static int PK_INDEX_ID; + protected static int MPK_INDEX_ID; + protected static int VIDX_INDEX_ID; + + private static final String[] setupScript = new String[] { + "box.schema.space.create('basic_test', { format = " + + "{{name = 'id', type = 'integer'}," + + " {name = 'val', type = 'string'} } })", + + "box.space.basic_test:create_index('pk', { type = 'TREE', parts = {'id'} } )", + "box.space.basic_test:create_index('vidx', { type = 'TREE', unique = false, parts = {'val'} } )", + + "box.space.basic_test:replace{1, 'one'}", + "box.space.basic_test:replace{2, 'two'}", + "box.space.basic_test:replace{3, 'three'}", + + "box.schema.space.create('multipart_test', { format = " + + "{{name = 'id1', type = 'integer'}," + + " {name = 'id2', type = 'string'}," + + " {name = 'val1', type = 'string'} } })", + + "box.space.multipart_test:create_index('pk', { type = 'TREE', parts = {'id1', 'id2'} })", + "box.space.multipart_test:create_index('vidx', { type = 'TREE', unique = false, parts = {'val1'} })", + + "box.space.multipart_test:replace{1, 'one', 'o n e'}", + "box.space.multipart_test:replace{2, 'two', 't w o'}", + "box.space.multipart_test:replace{3, 'three', 't h r e e'}", + + "function echo(...) return ... end" + }; + + private static final String[] cleanScript = new String[] { + "box.space.basic_test and box.space.basic_test:drop()", + "box.space.multipart_test and box.space.multipart_test:drop()" + }; + + @BeforeAll + public static void setupEnv() { + control = new TarantoolControl(); + control.start("jdk-testing"); + + console = openConsole(); + + executeLua(cleanScript); + executeLua(setupScript); + + SPACE_ID = console.eval("box.space.basic_test.id"); + PK_INDEX_ID = console.eval("box.space.basic_test.index.pk.id"); + VIDX_INDEX_ID = console.eval("box.space.basic_test.index.vidx.id"); + + MULTI_PART_SPACE_ID = console.eval("box.space.multipart_test.id"); + MPK_INDEX_ID = console.eval("box.space.multipart_test.index.pk.id"); + } + + @AfterAll + public static void cleanupEnv() { + executeLua(cleanScript); + + console.close(); + control.stop("jdk-testing"); + } + + private static void executeLua(String[] exprs) { + for (String expr : exprs) { + console.exec(expr); + } + } + + protected void checkTupleResult(Object res, List tuple) { + assertNotNull(res); + assertTrue(List.class.isAssignableFrom(res.getClass())); + List list = (List)res; + assertEquals(1, list.size()); + assertNotNull(list.get(0)); + assertTrue(List.class.isAssignableFrom(list.get(0).getClass())); + assertEquals(tuple, list.get(0)); + } + + protected TarantoolClient makeClient() { + TarantoolClientConfig config = new TarantoolClientConfig(); + config.username = username; + config.password = password; + config.initTimeoutMillis = 1000; + config.sharedBufferSize = 128; + + return new TarantoolClientImpl(socketChannelProvider, config); + } + + protected static TarantoolConsole openConsole() { + return TarantoolConsole.open(host, consolePort); + } + + protected static TarantoolConsole openConsole(String instance) { + return TarantoolConsole.open(control.tntCtlWorkDir, instance); + } + + protected TarantoolConnection openConnection() { + Socket socket = new Socket(); + try { + socket.connect(new InetSocketAddress(host, port)); + } catch (IOException e) { + throw new RuntimeException("Test failed due to invalid environment.", e); + } + try { + return new TarantoolConnection(username, password, socket); + } catch (Exception e) { + try { + socket.close(); + } catch (IOException ignored) { + // No-op. + } + throw new RuntimeException(e); + } + } + + protected List consoleSelect(String spaceName, Object key) { + StringBuilder sb = new StringBuilder("box.space."); + sb.append(spaceName); + sb.append(":select{"); + if (List.class.isAssignableFrom(key.getClass())) { + List parts = (List)key; + for (int i = 0; i < parts.size(); i++) { + if (i != 0) + sb.append(", "); + Object k = parts.get(i); + if (k.getClass().isAssignableFrom(String.class)) { + sb.append('\''); + sb.append(k); + sb.append('\''); + } else { + sb.append(k); + } + } + } else { + sb.append(key); + } + sb.append("}"); + return console.eval(sb.toString()); + } + + protected void stopTarantool(String instance) { + control.stop(instance); + } + + protected void startTarantool(String instance) { + control.start(instance); + } +} diff --git a/src/test/java/org/tarantool/AbstractTarantoolOpsIT.java b/src/test/java/org/tarantool/AbstractTarantoolOpsIT.java new file mode 100644 index 00000000..6df4a465 --- /dev/null +++ b/src/test/java/org/tarantool/AbstractTarantoolOpsIT.java @@ -0,0 +1,371 @@ +package org.tarantool; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Tests operations available in {@link TarantoolClientOps} interface. + */ +public abstract class AbstractTarantoolOpsIT extends AbstractTarantoolConnectorIT { + protected abstract TarantoolClientOps, Object, List> getOps(); + + @Test + public void testSelectOne() { + List res = getOps().select(SPACE_ID, PK_INDEX_ID, Collections.singletonList(1), 0, 1, Iterator.EQ); + checkTupleResult(res, Arrays.asList(1, "one")); + } + + @Test + public void testSelectMany() { + List res = getOps().select(SPACE_ID, PK_INDEX_ID, Collections.singletonList(10), 0, 10, + Iterator.LT); + + assertNotNull(res); + assertEquals(3, res.size()); + + // Descending order. + assertEquals(Arrays.asList(3, "three"), res.get(0)); + assertEquals(Arrays.asList(2, "two"), res.get(1)); + assertEquals(Arrays.asList(1, "one"), res.get(2)); + } + + @Test + public void testSelectOffsetLimit() { + List res = getOps().select(SPACE_ID, PK_INDEX_ID, Collections.singletonList(10), 1, 1, Iterator.LT); + assertNotNull(res); + assertEquals(1, res.size()); + + assertEquals(Arrays.asList(2, "two"), res.get(0)); + } + + @Test + public void testSelectUsingSecondaryIndex() { + List res = getOps().select(SPACE_ID, VIDX_INDEX_ID, Collections.singletonList("one"), 0, 1, Iterator.EQ); + checkTupleResult(res, Arrays.asList(1, "one")); + } + + @Test + public void testInsertSimple() { + List tup = Arrays.asList(100, "hundred"); + List res = getOps().insert(SPACE_ID, tup); + + checkTupleResult(res, tup); + + // Check it actually was inserted. + checkTupleResult(consoleSelect(SPACE_NAME, 100), tup); + } + + @Test + public void testInsertMultiPart() { + List tup = Arrays.asList(100, "hundred", "h u n d r e d"); + List res = getOps().insert(MULTI_PART_SPACE_ID, tup); + + checkTupleResult(res, tup); + + // Check it actually was inserted. + checkTupleResult(consoleSelect(MULTIPART_SPACE_NAME, Arrays.asList(100, "hundred")), tup); + } + + @Test + public void testReplaceSimple() { + checkReplace(SPACE_NAME, + SPACE_ID, + Collections.singletonList(10), + Arrays.asList(10, "10"), + Arrays.asList(10, "ten")); + } + + @Test + public void testReplaceMultiPartKey() { + checkReplace(MULTIPART_SPACE_NAME, + MULTI_PART_SPACE_ID, + Arrays.asList(10, "10"), + Arrays.asList(10, "10", "10"), + Arrays.asList(10, "10", "ten")); + } + + private void checkReplace(String space, int spaceId, List key, List createTuple, List updateTuple) { + List res = getOps().replace(spaceId, createTuple); + checkTupleResult(res, createTuple); + + // Check it actually was created. + checkTupleResult(consoleSelect(space, key), createTuple); + + // Update + res = getOps().replace(spaceId, updateTuple); + checkTupleResult(res, updateTuple); + + // Check it actually was updated. + checkTupleResult(consoleSelect(space, key), updateTuple); + } + + @Test + public void testUpdateNonExistingHasNoEffect() { + List op0 = Arrays.asList("=", 3, "trez"); + + List res = getOps().update(SPACE_ID, Collections.singletonList(30), op0); + + assertNotNull(res); + assertEquals(0, res.size()); + + // Check it doesn't exist. + res = getOps().select(SPACE_ID, PK_INDEX_ID, Collections.singletonList(30), 0, 1, Iterator.EQ); + assertNotNull(res); + assertEquals(0, res.size()); + } + + @Test + public void testUpdate() { + checkUpdate(SPACE_NAME, + SPACE_ID, + // key + Collections.singletonList(30), + // init tuple + Arrays.asList(30, "30"), + // expected tuple + Arrays.asList(30, "thirty"), + // operations + Arrays.asList("!", 1, "thirty"), + Arrays.asList("#", 2, 1)); + } + + @Test + public void testUpdateMultiPart() { + checkUpdate(MULTIPART_SPACE_NAME, + MULTI_PART_SPACE_ID, + Arrays.asList(30, "30"), + Arrays.asList(30, "30", "30"), + Arrays.asList(30, "30", "thirty"), + Arrays.asList("=", 2, "thirty")); + } + + private void checkUpdate(String space, int spaceId, List key, List initTuple, List expectedTuple, + Object ... ops) { + // Try update non-existing key. + List res = getOps().update(spaceId, key, ops); + assertNotNull(res); + assertEquals(0, res.size()); + + // Check it still doesn't exists. + assertEquals(Collections.emptyList(), consoleSelect(space, key)); + + // Create the tuple. + res = getOps().insert(spaceId, initTuple); + checkTupleResult(res, initTuple); + + // Apply the update operations. + res = getOps().update(spaceId, key, ops); + checkTupleResult(res, expectedTuple); + + // Check that update was actually performed. + checkTupleResult(consoleSelect(space, key), expectedTuple); + } + + @Test + public void testUpsertSimple() { + checkUpsert(SPACE_NAME, + SPACE_ID, + Collections.singletonList(40), + Arrays.asList(40, "40"), + Arrays.asList(40, "fourty"), + Arrays.asList("=", 1, "fourty")); + } + + @Test + public void testUpsertMultiPart() { + checkUpsert(MULTIPART_SPACE_NAME, + MULTI_PART_SPACE_ID, + Arrays.asList(40, "40"), + Arrays.asList(40, "40", "40"), + Arrays.asList(40, "40", "fourty"), + Arrays.asList("=", 2, "fourty")); + } + + private void checkUpsert(String space, int spaceId, List key, List defTuple, List expectedTuple, + Object ... ops) { + // Check that key doesn't exist. + assertEquals(Collections.emptyList(), consoleSelect(space, key)); + + // Try upsert non-existing key. + List res = getOps().upsert(spaceId, key, defTuple, ops); + assertNotNull(res); + assertEquals(0, res.size()); + + // Check that default tuple was inserted. + checkTupleResult(consoleSelect(space, key), defTuple); + + // Apply the operations. + res = getOps().upsert(spaceId, key, defTuple, ops); + assertNotNull(res); + assertEquals(0, res.size()); + + // Check that update was actually performed. + checkTupleResult(consoleSelect(space, key), expectedTuple); + } + + @Test + public void testDeleteSimple() { + checkDelete(SPACE_NAME, + SPACE_ID, + Collections.singletonList(50), + Arrays.asList(50, "fifty")); + } + + @Test + public void testDeleteMultiPartKey() { + checkDelete(MULTIPART_SPACE_NAME, + MULTI_PART_SPACE_ID, + Arrays.asList(50, "50"), + Arrays.asList(50, "50", "fifty")); + } + + private void checkDelete(String space, int spaceId, List key, List tuple) { + // Check the key doesn't exists. + assertEquals(Collections.emptyList(), consoleSelect(space, key)); + + // Try to delete non-existing key. + List res = getOps().delete(spaceId, key); + assertNotNull(res); + assertEquals(0, res.size()); + + // Create tuple. + res = getOps().insert(spaceId, tuple); + checkTupleResult(res, tuple); + + // Check the tuple was created. + checkTupleResult(consoleSelect(space, key), tuple); + + // Delete it. + res = getOps().delete(spaceId, key); + checkTupleResult(res, tuple); + + // Check it actually was deleted. + assertEquals(Collections.emptyList(), consoleSelect(space, key)); + } + + @Test + public void testEval() { + assertEquals(Collections.singletonList("true"), getOps().eval("return echo(...)", "true")); + } + + @Test + public void testCall() { + assertEquals(Collections.singletonList(Collections.singletonList("true")), getOps().call("echo", "true")); + } + + @Test + public void testPing() { + getOps().ping(); + } + + @Test + public void testDeleteFromNonExistingSpace() { + TarantoolException ex = assertThrows(TarantoolException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().delete(5555, Collections.singletonList(2)); + } + }); + assertEquals("Space '5555' does not exist", ex.getMessage()); + } + + @Test + public void testSelectUnsupportedIterator() { + TarantoolException ex = assertThrows(TarantoolException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().select(SPACE_ID, PK_INDEX_ID, Collections.singletonList(1), 0, 1, Iterator.OVERLAPS); + } + }); + assertEquals( + "Index 'pk' (TREE) of space 'basic_test' (memtx) does not support requested iterator type", + ex.getMessage()); + } + + @Test + public void testSelectNonExistingKey() { + List res = getOps().select(SPACE_ID, PK_INDEX_ID, Collections.singletonList(5555), 0, 1, + Iterator.EQ); + + assertNotNull(res); + assertEquals(0, res.size()); + } + + @Test + public void testSelectFromNonExistingIndex() { + TarantoolException ex = assertThrows(TarantoolException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().select(SPACE_ID, 5555, Collections.singletonList(2), 0, 1, Iterator.EQ); + } + }); + assertEquals("No index #5555 is defined in space 'basic_test'", ex.getMessage()); + } + + @Test + public void testSelectFromNonExistingSpace() { + TarantoolException ex = assertThrows(TarantoolException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().select(5555, 0, Collections.singletonList(5555), 0, 1, Iterator.EQ); + } + }); + assertEquals("Space '5555' does not exist", ex.getMessage()); + } + + @Test + public void testInsertDuplicateKey() { + final List tup = Arrays.asList(1, "uno"); + TarantoolException ex = assertThrows(TarantoolException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().insert(SPACE_ID, tup); + } + }); + assertEquals("Duplicate key exists in unique index 'pk' in space 'basic_test'", ex.getMessage()); + + // Check the tuple stayed intact. + checkTupleResult(consoleSelect(SPACE_NAME, 1), Arrays.asList(1, "one")); + } + + @Test + public void testInsertToNonExistingSpace() { + TarantoolException ex = assertThrows(TarantoolException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().insert(5555, Arrays.asList(1, "one")); + } + }); + assertEquals("Space '5555' does not exist", ex.getMessage()); + } + + @Test + public void testInsertInvalidData() { + // Invalid types. + TarantoolException ex = assertThrows(TarantoolException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().insert(SPACE_ID, Arrays.asList("one", 1)); + } + }); + assertEquals("Tuple field 1 type does not match one required by operation: expected integer", ex.getMessage()); + + // Invalid tuple size. + ex = assertThrows(TarantoolException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().insert(SPACE_ID, Collections.singletonList("one")); + } + }); + assertEquals("Tuple field count 1 is less than required by space format or defined indexes " + + "(expected at least 2)", ex.getMessage()); + } +} diff --git a/src/test/java/org/tarantool/AsyncClientOperationsIT.java b/src/test/java/org/tarantool/AsyncClientOperationsIT.java new file mode 100644 index 00000000..5d7852a2 --- /dev/null +++ b/src/test/java/org/tarantool/AsyncClientOperationsIT.java @@ -0,0 +1,119 @@ +package org.tarantool; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for asynchronous operations provided by {@link TarantoolClientImpl} class. + */ +public class AsyncClientOperationsIT extends AbstractTarantoolConnectorIT { + private TarantoolClient client; + + @BeforeEach + public void setup() { + client = makeClient(); + } + + @AfterEach + public void tearDown() { + client.close(); + } + + @Test + public void testPing() { + // This ping is still synchronous due to API declaration returning void. + client.asyncOps().ping(); + } + + @Test + public void testClose() { + assertTrue(client.isAlive()); + client.asyncOps().close(); + assertFalse(client.isAlive()); + } + + @Test + public void testAsyncError() { + // Attempt to insert duplicate key. + final Future> res = client.asyncOps().insert(SPACE_ID, Arrays.asList(1, "one")); + + // Check that error is delivered when trying to obtain future result. + ExecutionException e = assertThrows(ExecutionException.class, new Executable() { + @Override + public void execute() throws ExecutionException, InterruptedException, TimeoutException { + res.get(TIMEOUT, TimeUnit.MILLISECONDS); + } + }); + assertNotNull(e.getCause()); + assertTrue(TarantoolException.class.isAssignableFrom(e.getCause().getClass())); + } + + @Test + public void testOperations() throws ExecutionException, InterruptedException, TimeoutException { + TarantoolClientOps, Object, Future>> ops = client.asyncOps(); + + List>> futs = new ArrayList>>(); + + futs.add(ops.insert(SPACE_ID, Arrays.asList(10, "10"))); + futs.add(ops.delete(SPACE_ID, Collections.singletonList(10))); + + futs.add(ops.insert(SPACE_ID, Arrays.asList(10, "10"))); + futs.add(ops.update(SPACE_ID, Collections.singletonList(10), Arrays.asList("=", 1, "ten"))); + + futs.add(ops.replace(SPACE_ID, Arrays.asList(20, "20"))); + futs.add(ops.upsert(SPACE_ID, Collections.singletonList(20), Arrays.asList(20, "twenty"), + Arrays.asList("=", 1, "twenty"))); + + futs.add(ops.insert(SPACE_ID, Arrays.asList(30, "30"))); + futs.add(ops.call("box.space.basic_test:delete", Collections.singletonList(30))); + + // Wait completion of all operations. + for (Future> f : futs) + f.get(TIMEOUT, TimeUnit.MILLISECONDS); + + // Check the effects. + checkTupleResult(consoleSelect(SPACE_NAME, 10), Arrays.asList(10, "ten")); + checkTupleResult(consoleSelect(SPACE_NAME, 20), Arrays.asList(20, "twenty")); + assertEquals(consoleSelect(SPACE_NAME, 30), Collections.emptyList()); + } + + @Test + public void testSelect() throws ExecutionException, InterruptedException, TimeoutException { + Future> fut = client.asyncOps().select(SPACE_ID, PK_INDEX_ID, Collections.singletonList(1), 0, 1, + Iterator.EQ); + + List res = fut.get(TIMEOUT, TimeUnit.MILLISECONDS); + + checkTupleResult(res, Arrays.asList(1, "one")); + } + + @Test + public void testEval() throws ExecutionException, InterruptedException, TimeoutException { + Future> fut = client.asyncOps().eval("return true"); + assertEquals(Collections.singletonList(true), fut.get(TIMEOUT, TimeUnit.MILLISECONDS)); + } + + @Test + public void testCall() throws ExecutionException, InterruptedException, TimeoutException { + Future> fut = client.asyncOps().call("echo", "hello"); + assertEquals(Collections.singletonList(Collections.singletonList("hello")), + fut.get(TIMEOUT, TimeUnit.MILLISECONDS)); + } +} diff --git a/src/test/java/org/tarantool/ClientOperationsIT.java b/src/test/java/org/tarantool/ClientOperationsIT.java new file mode 100644 index 00000000..22d1965a --- /dev/null +++ b/src/test/java/org/tarantool/ClientOperationsIT.java @@ -0,0 +1,46 @@ +package org.tarantool; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Tests for synchronous operations of {@link TarantoolClientImpl} class. + * + * Actual tests reside in base class. + */ +public class ClientOperationsIT extends AbstractTarantoolOpsIT { + private TarantoolClient client; + + @BeforeEach + public void setup() { + client = makeClient(); + } + + @AfterEach + public void tearDown() { + client.close(); + } + + @Override + protected TarantoolClientOps, Object, List> getOps() { + return client.syncOps(); + } + + @Test + public void testClose() { + IllegalStateException e = assertThrows(IllegalStateException.class, new Executable() { + @Override + public void execute() throws Throwable { + getOps().close(); + } + }); + assertEquals(e.getMessage(), "You should close TarantoolClient instead."); + } +} diff --git a/src/test/java/org/tarantool/ClientReconnectIT.java b/src/test/java/org/tarantool/ClientReconnectIT.java new file mode 100644 index 00000000..46b02b01 --- /dev/null +++ b/src/test/java/org/tarantool/ClientReconnectIT.java @@ -0,0 +1,59 @@ +package org.tarantool; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ClientReconnectIT extends AbstractTarantoolConnectorIT { + private static final String INSTANCE_NAME = "jdk-testing"; + private TarantoolClient client; + + @BeforeEach + public void setup() { + client = makeClient(); + } + + @AfterEach + public void tearDown() { + client.close(); + + // Re-open console for cleanupEnv() to work. + console.close(); + console = openConsole(); + } + + @Test + public void testReconnect() throws Exception { + client.syncOps().ping(); + + stopTarantool(INSTANCE_NAME); + + Exception e = assertThrows(Exception.class, new Executable() { + @Override + public void execute() { + client.syncOps().ping(); + } + }); + + assertTrue(CommunicationException.class.isAssignableFrom(e.getClass()) || + IllegalStateException.class.isAssignableFrom(e.getClass())); + + assertNotNull(((TarantoolClientImpl) client).getThumbstone()); + + assertFalse(client.isAlive()); + + startTarantool(INSTANCE_NAME); + + assertTrue(client.waitAlive(TIMEOUT, TimeUnit.MILLISECONDS)); + + client.syncOps().ping(); + } +} diff --git a/src/test/java/org/tarantool/ConnectionIT.java b/src/test/java/org/tarantool/ConnectionIT.java new file mode 100644 index 00000000..0d70342c --- /dev/null +++ b/src/test/java/org/tarantool/ConnectionIT.java @@ -0,0 +1,36 @@ +package org.tarantool; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +/** + * Test operations of {@link TarantoolConnection} class. + * + * Actual tests reside in base class. + */ +public class ConnectionIT extends AbstractTarantoolOpsIT { + private TarantoolConnection conn; + + @BeforeEach + public void setup() { + conn = openConnection(); + } + + @AfterEach + public void tearDown() { + conn.close(); + } + + @Override + protected TarantoolClientOps, Object, List> getOps() { + return conn; + } + + @Test + public void testClose() { + conn.close(); + } +} diff --git a/src/test/java/org/tarantool/FireAndForgetClientOperationsIT.java b/src/test/java/org/tarantool/FireAndForgetClientOperationsIT.java new file mode 100644 index 00000000..3d81fbe2 --- /dev/null +++ b/src/test/java/org/tarantool/FireAndForgetClientOperationsIT.java @@ -0,0 +1,83 @@ +package org.tarantool; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Test "fire & forget" operations available in {@link TarantoolClientImpl} class. + */ +public class FireAndForgetClientOperationsIT extends AbstractTarantoolConnectorIT { + private TarantoolClient client; + + @BeforeEach + public void setup() { + client = makeClient(); + } + + @AfterEach + public void tearDown() { + client.close(); + } + + @Test + public void testPing() { + // Half-ping actually. + client.fireAndForgetOps().ping(); + } + + @Test + public void testClose() { + IllegalStateException e = assertThrows(IllegalStateException.class, new Executable() { + @Override + public void execute() throws Throwable { + client.fireAndForgetOps().close(); + } + }); + assertEquals(e.getMessage(), "You should close TarantoolClient instead."); + } + + @Test + public void testFireAndForgetOperations() { + TarantoolClientOps, Object, Long> ffOps = client.fireAndForgetOps(); + + Set syncIds = new HashSet(); + + syncIds.add(ffOps.insert(SPACE_ID, Arrays.asList(10, "10"))); + syncIds.add(ffOps.delete(SPACE_ID, Collections.singletonList(10))); + + syncIds.add(ffOps.insert(SPACE_ID, Arrays.asList(10, "10"))); + syncIds.add(ffOps.update(SPACE_ID, Collections.singletonList(10), Arrays.asList("=", 1, "ten"))); + + syncIds.add(ffOps.replace(SPACE_ID, Arrays.asList(20, "20"))); + syncIds.add(ffOps.upsert(SPACE_ID, Collections.singletonList(20), Arrays.asList(20, "twenty"), + Arrays.asList("=", 1, "twenty"))); + + syncIds.add(ffOps.insert(SPACE_ID, Arrays.asList(30, "30"))); + syncIds.add(ffOps.call("box.space.basic_test:delete", Collections.singletonList(30))); + + // Check the syncs. + assertFalse(syncIds.contains(0L)); + assertEquals(8, syncIds.size()); + + // The reply for synchronous ping will + // indicate to us that previous fire & forget operations are completed. + client.syncOps().ping(); + + // Check the effects + checkTupleResult(consoleSelect(SPACE_NAME, 10), Arrays.asList(10, "ten")); + checkTupleResult(consoleSelect(SPACE_NAME, 20), Arrays.asList(20, "twenty")); + assertEquals(consoleSelect(SPACE_NAME, 30), Collections.emptyList()); + } +} diff --git a/src/test/java/org/tarantool/TarantoolConsole.java b/src/test/java/org/tarantool/TarantoolConsole.java new file mode 100644 index 00000000..9ae1106b --- /dev/null +++ b/src/test/java/org/tarantool/TarantoolConsole.java @@ -0,0 +1,241 @@ +package org.tarantool; + +import org.yaml.snakeyaml.Yaml; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Blocking console connection for test control purposes. + * + * Provides the means of lua commands evaluation given + * the host and port or the instance name of tarantool. + */ +public abstract class TarantoolConsole implements Closeable { + private final static Pattern GREETING_PATTERN = Pattern.compile("^Tarantool.+\n.+\n"); + private final static Pattern CONNECTED_PATTERN = Pattern.compile("^connected to (.*)\n"); + private final static Pattern REPLY_PATTERN = Pattern.compile("^.*\\n\\.{3}\\n", + Pattern.UNIX_LINES | Pattern.DOTALL); + + private final static int TIMEOUT = 2000; + private final StringBuilder unmatched = new StringBuilder(); + + protected BufferedReader reader; + protected OutputStreamWriter writer; + + private Matcher checkMatch(Pattern p) { + if (unmatched.length() == 0) + return null; + + Matcher m = p.matcher(unmatched.toString()); + + if (m.find() && !m.requireEnd()) { + unmatched.delete(0, m.end()); + return m; + } + + return null; + } + + protected Matcher expect(Pattern p) { + Matcher result = checkMatch(p); + if (result != null) + return result; + + char[] buf = new char[4096]; + int rc; + try { + while ((rc = reader.read(buf, 0, buf.length)) > 0) { + appendApplyBackspaces(unmatched, buf, rc); + + result = checkMatch(p); + if (result != null) + return result; + } + } catch (SocketTimeoutException e) { + throw new RuntimeException("Timeout occurred. Unmatched: " + unmatched.toString() + ", pattern:" + p, e); + } catch (IOException e) { + throw new RuntimeException(e); + } + throw new RuntimeException("Unexpected end of input."); + } + + private static void appendApplyBackspaces(StringBuilder sb, char[] buf, int len) { + for (int i = 0; i < len ; i++) { + char c = buf[i]; + if (c == '\b') { + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + } else { + sb.append(c); + } + } + } + + protected void write(String expr) { + try { + writer.write(expr + '\n'); + writer.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void close() { + try { + reader.close(); + } catch (IOException e) { + // No-op. + } + try { + writer.close(); + } catch (IOException e) { + // No-op. + } + } + + protected void suppressPrompt() { + // No-op, override. + } + + protected void suppressEcho(String expr) { + // No-op, override. + } + + public void exec(String expr) { + suppressPrompt(); + write(expr); + suppressEcho(expr); + expect(REPLY_PATTERN); + } + + public T eval(String expr) { + suppressPrompt(); + write(expr); + suppressEcho(expr); + Matcher m = expect(REPLY_PATTERN); + Yaml yaml = new Yaml(); + List result = yaml.load(m.group(0)); + return result.get(0); + } + + /** + * A direct tarantool console connection. + * + * @param host Tarantool host name. + * @param port Console port of tarantool instance. + * @return Console connection object. + */ + public static TarantoolConsole open(String host, int port) { + return new TarantoolTcpConsole(host, port); + } + + /** + * An indirect tarantool console connection via tarantoolctl utility. + * + * > tarantoolctl enter <instance> + * + * This facility is aimed at support of multi-instance tests in future. + * + * @param workDir Directory where .tarantoolctl file is located. + * @param instance Tarantool instance name as per command. + * @return Console connection object. + */ + public static TarantoolConsole open(String workDir, String instance) { + return new TarantoolLocalConsole(workDir, instance); + } + + /** + * A direct tarantool console connection (via TCP connection). + */ + private static class TarantoolTcpConsole extends TarantoolConsole { + final Socket socket; + + TarantoolTcpConsole(String host, int port) { + socket = new TestSocketChannelProvider(host, port, TIMEOUT).get(1, null).socket(); + try { + reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + writer = new OutputStreamWriter(socket.getOutputStream()); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException ignored) { + // No-op. + } + throw new RuntimeException("Couldn't connect to console at " + host + ":" + port, e); + } + expect(GREETING_PATTERN); + } + + @Override + public void close() { + super.close(); + + try { + socket.close(); + } catch (IOException e) { + // No-op. + } + } + } + + /** + * An indirect tarantool console connection via tarantoolctl utility. + */ + private static class TarantoolLocalConsole extends TarantoolConsole { + final Process process; + final String name; + final Pattern prompt; + + TarantoolLocalConsole(String workDir, String instance) { + ProcessBuilder builder = new ProcessBuilder("env", "tarantoolctl", "enter", instance); + Map env = builder.environment(); + env.put("PWD", workDir); + env.put("TEST_WORKDIR", workDir); + env.put("COLUMNS", "256"); + builder.redirectErrorStream(true); + builder.directory(new File(workDir)); + + try { + process = builder.start(); + } catch (IOException e) { + throw new RuntimeException("environment failure", e); + } + reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + writer = new OutputStreamWriter(process.getOutputStream()); + + write("enter " + instance); + Matcher m = expect(CONNECTED_PATTERN); + name = m.group(1); + prompt = Pattern.compile(Pattern.quote(name + "> ")); + } + + @Override + protected void suppressPrompt() { + expect(prompt); + } + + @Override + protected void suppressEcho(String expr) { + expect(Pattern.compile(Pattern.quote(expr))); + } + + @Override + public void close() { + super.close(); + process.destroy(); + } + } +} diff --git a/src/test/java/org/tarantool/TarantoolControl.java b/src/test/java/org/tarantool/TarantoolControl.java new file mode 100644 index 00000000..e13b89e7 --- /dev/null +++ b/src/test/java/org/tarantool/TarantoolControl.java @@ -0,0 +1,173 @@ +package org.tarantool; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.Map; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Wrapper around tarantoolctl utility. + */ +public class TarantoolControl { + protected static final String tntCtlWorkDir = System.getProperty("tntCtlWorkDir", + new File("testroot").getAbsolutePath()); + protected static final String instanceDir = new File("src/test").getAbsolutePath(); + protected static final String tarantoolCtlConfig = new File("src/test/.tarantoolctl").getAbsolutePath(); + protected static final int RESTART_TIMEOUT = 2000; + + // Based on https://stackoverflow.com/a/779529 + private void rmdir(File f) throws IOException { + if (f.isDirectory()) { + for (File c : f.listFiles()) + rmdir(c); + } + f.delete(); + } + + private void rmdir(String f) throws IOException { + rmdir(new File(f)); + } + + private void mkdir(File f) throws IOException { + f.mkdirs(); + } + + private void mkdir(String f) throws IOException { + mkdir(new File(f)); + } + + private static void copyFile(File source, File dest) throws IOException { + if (dest.isDirectory()) + dest = new File(dest, source.getName()); + FileChannel sourceChannel = null; + FileChannel destChannel = null; + try { + sourceChannel = new FileInputStream(source).getChannel(); + destChannel = new FileOutputStream(dest).getChannel(); + destChannel.transferFrom(sourceChannel, 0, sourceChannel.size()); + } finally { + sourceChannel.close(); + destChannel.close(); + } + } + + private static void copyFile(String source, String dest) throws IOException { + copyFile(new File(source), new File(dest)); + } + + private static void copyFile(File source, String dest) throws IOException { + copyFile(source, new File(dest)); + } + + private static void copyFile(String source, File dest) throws IOException { + copyFile(new File(source), dest); + } + + private static String loadStream(InputStream s) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(s)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) + sb.append(line).append("\n"); + return sb.toString(); + } + + protected void setupWorkDirectory() throws IOException { + rmdir(tntCtlWorkDir); + mkdir(tntCtlWorkDir); + for (File c : new File(instanceDir).listFiles()) + if (c.getName().endsWith(".lua")) + copyFile(c, tntCtlWorkDir); + copyFile(tarantoolCtlConfig, tntCtlWorkDir); + } + + TarantoolControl() { + try { + setupWorkDirectory(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Control the given tarantool instance via tarantoolctl utility. + * + * @param command A tarantoolctl utility command. + * @param instanceName Name of tarantool instance to control. + */ + protected void executeCommand(String command, String instanceName) { + ProcessBuilder builder = new ProcessBuilder("env", "tarantoolctl", command, instanceName); + builder.directory(new File(tntCtlWorkDir)); + Map env = builder.environment(); + env.put("PWD", tntCtlWorkDir); + env.put("TEST_WORKDIR", tntCtlWorkDir); + + final Process process; + try { + process = builder.start(); + } catch (IOException e) { + throw new RuntimeException("environment failure", e); + } + + final CountDownLatch latch = new CountDownLatch(1); + // The thread below is necessary to organize timed wait on the process. + // We cannot use Process.waitFor(long, TimeUnit) because we on java 6. + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + process.waitFor(); + } catch (InterruptedException e) { + // No-op. + } + latch.countDown(); + } + }); + + thread.start(); + + boolean res; + try { + res = latch.await(RESTART_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new RuntimeException("wait interrupted", e); + } + + if (!res) { + thread.interrupt(); + process.destroy(); + + throw new RuntimeException("timeout"); + } + + int code = process.exitValue(); + + if (code != 0) { + String stdout = ""; + String stderr = ""; + try { + stdout = loadStream(process.getInputStream()); + stderr = loadStream(process.getErrorStream()); + } catch (IOException e) { + /* No-op. */ + } + throw new RuntimeException("returned exitcode " + code + "\n" + + "[stdout]\n" + stdout + "\n[stderr]\n" + stderr); + } + } + + public void start(String instanceName) { + executeCommand("start", instanceName); + } + + public void stop(String instanceName) { + executeCommand("stop", instanceName); + } +} diff --git a/src/test/java/org/tarantool/TestSocketChannelProvider.java b/src/test/java/org/tarantool/TestSocketChannelProvider.java new file mode 100644 index 00000000..924097a6 --- /dev/null +++ b/src/test/java/org/tarantool/TestSocketChannelProvider.java @@ -0,0 +1,40 @@ +package org.tarantool; + +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; + +/** + * Socket channel provider to be used throughout the tests. + */ +public class TestSocketChannelProvider implements SocketChannelProvider { + String host; + int port; + int restart_timeout; + + public TestSocketChannelProvider(String host, int port, int restart_timeout) { + this.host = host; + this.port = port; + this.restart_timeout = restart_timeout; + } + + @Override + public SocketChannel get(int retryNumber, Throwable lastError) { + long budget = System.currentTimeMillis() + restart_timeout; + while (!Thread.currentThread().isInterrupted()) { + try { + return SocketChannel.open(new InetSocketAddress(host, port)); + } catch (Exception e) { + if (budget < System.currentTimeMillis()) + throw new RuntimeException(e); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + // No-op. + Thread.currentThread().interrupt(); + } + } + } + throw new RuntimeException("Test failure due to invalid environment. " + + "Timeout connecting to " + host + ":" + port); + } +} diff --git a/src/test/instance.lua b/src/test/jdk-testing.lua similarity index 63% rename from src/test/instance.lua rename to src/test/jdk-testing.lua index fe8a80bc..fa6615d9 100644 --- a/src/test/instance.lua +++ b/src/test/jdk-testing.lua @@ -1,7 +1,5 @@ -time = require('clock').time - box.cfg { - listen = '0.0.0.0:3301', + listen = 3301, } box.once('init', function() @@ -16,7 +14,11 @@ box.once('init', function() box.schema.user.create('test_ordin', { password = '2HWRXHfa' }) box.schema.user.create('test_admin', { password = '4pWBZmLEgkmKK5WP' }) - box.schema.user.grant('test_ordin', 'read,write', 'user') - box.schema.user.grant('test_admin', 'execute', 'super') + box.schema.user.grant('test_ordin', 'read,write', 'space', 'user') + box.schema.user.grant('test_admin', 'super') end) +-- Java has no internal support for unix domain sockets, +-- so we will use tcp for console communication. +console = require('console') +console.listen(3313) diff --git a/src/test/travis.pre.sh b/src/test/travis.pre.sh index 86ecdd3b..cdc7ca3e 100755 --- a/src/test/travis.pre.sh +++ b/src/test/travis.pre.sh @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e curl http://download.tarantool.org/tarantool/1.9/gpgkey | sudo apt-key add - release=`lsb_release -c -s` @@ -14,6 +14,4 @@ EOF sudo apt-get update sudo apt-get -y install tarantool tarantool-common -sudo cp src/test/instance.lua /etc/tarantool/instances.enabled/jdk-testing.lua -sudo tarantoolctl stop example -sudo tarantoolctl start jdk-testing +sudo tarantoolctl stop example