diff --git a/.github/workflows/bindings_java.yml b/.github/workflows/bindings_java.yml index 21b68607bf43..3aadb5a67869 100644 --- a/.github/workflows/bindings_java.yml +++ b/.github/workflows/bindings_java.yml @@ -84,8 +84,4 @@ jobs: # https://maven.apache.org/guides/mini/guide-reproducible-builds.html#how-to-test-my-maven-build-reproducibility shell: bash run: | - if [[ "${{ runner.os }}" != Linux ]]; then - export NO_DOCKER=true - fi ./mvnw clean install -DskipTests -Dcargo-build.features=services-redis - ./mvnw verify artifact:compare -Dcargo-build.features=services-redis diff --git a/.github/workflows/service_test_redis.yml b/.github/workflows/service_test_redis.yml index 85e6b48ef369..4b13ee770696 100644 --- a/.github/workflows/service_test_redis.yml +++ b/.github/workflows/service_test_redis.yml @@ -192,3 +192,26 @@ jobs: OPENDAL_REDIS_ENDPOINT: tcp://127.0.0.1:6379 OPENDAL_REDIS_ROOT: / OPENDAL_REDIS_DB: 0 + + binding_java_redis: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup Rust toolchain + uses: ./.github/actions/setup + + - name: Setup Redis Server + shell: bash + working-directory: fixtures/redis + run: docker-compose -f docker-compose-redis.yml up -d + + - name: Test + shell: bash + working-directory: bindings/java + run: ./mvnw test -Dtest=org.apache.opendal.OperatorTest -Dcargo-build.features=services-redis + env: + OPENDAL_REDIS_TEST: on + OPENDAL_REDIS_ENDPOINT: tcp://127.0.0.1:6379 + OPENDAL_REDIS_ROOT: / + OPENDAL_REDIS_DB: 0 diff --git a/.github/workflows/service_test_s3.yml b/.github/workflows/service_test_s3.yml index ebaed5b886ad..a6ad92d0588b 100644 --- a/.github/workflows/service_test_s3.yml +++ b/.github/workflows/service_test_s3.yml @@ -242,3 +242,24 @@ jobs: # This is the R2's limitation # Refer to https://opendal.apache.org/docs/services/s3#compatible-services for more information OPENDAL_S3_ENABLE_EXACT_BUF_WRITE: true + + binding_java_s3: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup Rust toolchain + uses: ./.github/actions/setup + + - name: Test + shell: bash + working-directory: bindings/java + run: ./mvnw test -Dtest=org.apache.opendal.OperatorTest + env: + OPENDAL_S3_TEST: ${{ secrets.OPENDAL_S3_TEST }} + OPENDAL_S3_ROOT: ${{ secrets.OPENDAL_S3_ROOT }} + OPENDAL_S3_BUCKET: ${{ secrets.OPENDAL_S3_BUCKET }} + OPENDAL_S3_ENDPOINT: ${{ secrets.OPENDAL_S3_ENDPOINT }} + OPENDAL_S3_ACCESS_KEY_ID: ${{ secrets.OPENDAL_S3_ACCESS_KEY_ID }} + OPENDAL_S3_SECRET_ACCESS_KEY: ${{ secrets.OPENDAL_S3_SECRET_ACCESS_KEY }} + OPENDAL_S3_REGION: ap-northeast-1 diff --git a/bindings/java/README.md b/bindings/java/README.md index 6523b00d47cc..ef4f65107791 100644 --- a/bindings/java/README.md +++ b/bindings/java/README.md @@ -64,12 +64,42 @@ You can use Maven to build both Rust dynamic lib and JAR files with one command ## Run tests -Currently, all tests are written in Java. It contains the Cucumber feature tests and other unit tests. +Currently, all tests are written in Java. -You can run tests with the following command: +You can run the base tests with the following command: ```shell -./mvnw clean verify -Dcargo-build.features=services-redis +./mvnw clean verify +``` + +## Run Service Tests + +Please copy `{project.rootdir}/.env.example` to `{project.rootdir}/.env` and change the values on need. + +Take `fs` for example, we need to enable bench on `fs` on `/tmp`. + +```dotenv +OPENDAL_FS_TEST=false +OPENDAL_FS_ROOT=/path/to/dir +``` + +into + +```dotenv +OPENDAL_FS_TEST=on +OPENDAL_FS_ROOT=/opendal +``` + +You can run service tests of enabled with the following command: + +```shell +./mvnw test -Dtest=org.apache.opendal.OperatorTest +``` + +You can run the unbound service with the following command: + +```shell +./mvnw test -Dtest=org.apache.opendal.OperatorTest -Dcargo-build.features=services-redis ``` > **Note:** diff --git a/bindings/java/src/test/java/org/apache/opendal/AsyncStepsTest.java b/bindings/java/src/test/java/org/apache/opendal/AsyncStepsTest.java deleted file mode 100644 index 6d542fa5dbd2..000000000000 --- a/bindings/java/src/test/java/org/apache/opendal/AsyncStepsTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.opendal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import io.cucumber.java.en.Given; -import io.cucumber.java.en.Then; -import io.cucumber.java.en.When; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import lombok.Cleanup; -import org.apache.opendal.condition.OpenDALExceptionCondition; - -public class AsyncStepsTest { - Operator op; - - @Given("A new OpenDAL Async Operator") - public void a_new_open_dal_async_operator() { - Map params = new HashMap<>(); - params.put("root", "/tmp"); - op = new Operator("Memory", params); - } - - @When("Async write path {string} with content {string}") - public void async_write_path_test_with_content_hello_world(String path, String content) { - op.write(path, content).join(); - } - - @Then("The async file {string} should exist") - public void the_async_file_test_should_exist(String path) { - @Cleanup Metadata metadata = op.stat(path).join(); - assertNotNull(metadata); - } - - @Then("The async file {string} entry mode must be file") - public void the_async_file_test_entry_mode_must_be_file(String path) { - @Cleanup Metadata metadata = op.stat(path).join(); - assertTrue(metadata.isFile()); - } - - @Then("The async file {string} content length must be {int}") - public void the_async_file_test_content_length_must_be_13(String path, int length) { - @Cleanup Metadata metadata = op.stat(path).join(); - assertEquals(metadata.getContentLength(), length); - } - - @Then("The async file {string} must have content {string}") - public void the_async_file_test_must_have_content_hello_world(String path, String content) { - byte[] readContent = op.read(path).join(); - assertThat(readContent).isEqualTo(content.getBytes(StandardCharsets.UTF_8)); - } - - @Then("The presign operation should success or raise exception Unsupported") - public void the_presign_operation_should_success_or_raise_exception_unsupported() { - assertThatThrownBy( - () -> op.presignStat("test.txt", Duration.ofSeconds(10)).join()) - .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.Unsupported)); - assertThatThrownBy( - () -> op.presignRead("test.txt", Duration.ofSeconds(10)).join()) - .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.Unsupported)); - assertThatThrownBy(() -> - op.presignWrite("test.txt", Duration.ofSeconds(10)).join()) - .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.Unsupported)); - } -} diff --git a/bindings/java/src/test/java/org/apache/opendal/BlockingOperatorTest.java b/bindings/java/src/test/java/org/apache/opendal/BlockingOperatorTest.java deleted file mode 100644 index 8e93ec98ea17..000000000000 --- a/bindings/java/src/test/java/org/apache/opendal/BlockingOperatorTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.opendal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class BlockingOperatorTest { - private BlockingOperator op; - - @BeforeEach - public void init() { - Map params = new HashMap<>(); - params.put("root", "/tmp"); - this.op = new BlockingOperator("Memory", params); - } - - @AfterEach - public void clean() { - this.op.close(); - } - - @Test - public void testStatNotExistFile() { - assertThatExceptionOfType(OpenDALException.class) - .isThrownBy(() -> op.stat("nonexistence")) - .extracting(OpenDALException::getCode) - .isEqualTo(OpenDALException.Code.NotFound); - } - - @Test - public void testCreateAndDelete() { - op.write("testCreateAndDelete", "Odin"); - assertThat(op.read("testCreateAndDelete")).isEqualTo("Odin".getBytes(StandardCharsets.UTF_8)); - op.delete("testCreateAndDelete"); - assertThatExceptionOfType(OpenDALException.class) - .isThrownBy(() -> op.stat("testCreateAndDelete")) - .extracting(OpenDALException::getCode) - .isEqualTo(OpenDALException.Code.NotFound); - } -} diff --git a/bindings/java/src/test/java/org/apache/opendal/CucumberTest.java b/bindings/java/src/test/java/org/apache/opendal/CucumberTest.java deleted file mode 100644 index ab8ae9c31f69..000000000000 --- a/bindings/java/src/test/java/org/apache/opendal/CucumberTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.opendal; - -import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; -import org.junit.platform.suite.api.ConfigurationParameter; -import org.junit.platform.suite.api.IncludeEngines; -import org.junit.platform.suite.api.SelectClasspathResource; -import org.junit.platform.suite.api.Suite; - -@Suite -@IncludeEngines("cucumber") -@SelectClasspathResource("features") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "org.apache.opendal") -public class CucumberTest {} diff --git a/bindings/java/src/test/java/org/apache/opendal/OperatorTest.java b/bindings/java/src/test/java/org/apache/opendal/OperatorTest.java index e12eef983009..f77529cdd793 100644 --- a/bindings/java/src/test/java/org/apache/opendal/OperatorTest.java +++ b/bindings/java/src/test/java/org/apache/opendal/OperatorTest.java @@ -21,57 +21,247 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.Cleanup; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Stream; import org.apache.opendal.condition.OpenDALExceptionCondition; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; +import org.apache.opendal.utils.Utils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class OperatorTest { - @TempDir - private static Path tempDir; - - @Test - public void testCreateAndDelete() { - Map params = new HashMap<>(); - params.put("root", "/tmp"); - @Cleanup Operator op = new Operator("Memory", params); - - op.write("testCreateAndDelete", "Odin").join(); - assertThat(op.read("testCreateAndDelete").join()).isEqualTo("Odin".getBytes(StandardCharsets.UTF_8)); - op.delete("testCreateAndDelete").join(); - assertThatThrownBy(() -> op.stat("testCreateAndDelete").join()) + + private static final List ops = new ArrayList<>(); + + private static final List blockingOps = new ArrayList<>(); + + protected static final String[] schemas = new String[] { + "atomicserver", + "azblob", + "azdls", + "cacache", + "cos", + "dashmap", + "etcd", + "foundationdb", + "fs", + "ftp", + "gcs", + "ghac", + "hdfs", + "http", + "ipfs", + "ipmfs", + "memcached", + "memory", + "minimoka", + "moka", + "obs", + "onedrive", + "gdrive", + "dropbox", + "oss", + "persy", + "redis", + "postgresql", + "rocksdb", + "s3", + "sftp", + "sled", + "supabase", + "vercel-artifacts", + "wasabi", + "webdav", + "webhdfs", + "redb", + "tikv", + }; + + @BeforeAll + public static void init() { + for (String schema : schemas) { + + Optional opOptional = Utils.init(schema); + opOptional.ifPresent(op -> ops.add(op)); + + Optional blockingOpOptional = Utils.initBlockingOp(schema); + blockingOpOptional.ifPresent(op -> blockingOps.add(op)); + } + if (ops.isEmpty()) { + ops.add(null); + } + if (blockingOps.isEmpty()) { + blockingOps.add(null); + } + } + + @AfterAll + public static void clean() { + ops.stream().filter(Objects::nonNull).forEach(Operator::close); + blockingOps.stream().filter(Objects::nonNull).forEach(BlockingOperator::close); + } + + private static Stream getOperators() { + return ops.stream(); + } + + private static Stream getBlockingOperators() { + return blockingOps.stream(); + } + + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getBlockingOperators") + public void testBlockingWrite(BlockingOperator blockingOp) { + assumeTrue(blockingOp != null); + + Capability cap = blockingOp.info().fullCapability; + if (!cap.write || !cap.read) { + return; + } + + String path = UUID.randomUUID().toString(); + byte[] content = Utils.generateBytes(); + blockingOp.write(path, content); + + Metadata metadata = blockingOp.stat(path); + + assertEquals(content.length, metadata.getContentLength()); + + blockingOp.delete(path); + assertThatThrownBy(() -> blockingOp.stat(path)) + .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.NotFound)); + } + + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getBlockingOperators") + public void testBlockingRead(BlockingOperator blockingOp) { + assumeTrue(blockingOp != null); + + Capability cap = blockingOp.info().fullCapability; + if (!cap.write || !cap.read) { + return; + } + + Metadata metadata = blockingOp.stat(""); + assertTrue(!metadata.isFile()); + + String path = UUID.randomUUID().toString(); + assertThatThrownBy(() -> blockingOp.stat(path)) + .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.NotFound)); + + byte[] content = Utils.generateBytes(); + blockingOp.write(path, content); + + assertThat(blockingOp.read(path)).isEqualTo(content); + + blockingOp.delete(path); + assertThatThrownBy(() -> blockingOp.stat(path)) + .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.NotFound)); + } + + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getOperators") + public final void testWrite(Operator op) throws Exception { + assumeTrue(op != null); + + Capability cap = op.info().fullCapability; + if (!cap.write || !cap.read) { + return; + } + + String path = UUID.randomUUID().toString(); + byte[] content = Utils.generateBytes(); + op.write(path, content).join(); + + Metadata metadata = op.stat(path).get(); + + assertEquals(content.length, metadata.getContentLength()); + + op.delete(path).join(); + assertThatThrownBy(() -> op.stat(path).join()) + .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.NotFound)); + } + + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getOperators") + public final void testRead(Operator op) throws Exception { + assumeTrue(op != null); + + Capability cap = op.info().fullCapability; + if (!cap.write || !cap.read) { + return; + } + + Metadata metadata = op.stat("").get(); + assertTrue(!metadata.isFile()); + + String path = UUID.randomUUID().toString(); + assertThatThrownBy(() -> op.stat(path).join()) + .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.NotFound)); + + byte[] content = Utils.generateBytes(); + op.write(path, content).join(); + + assertThat(op.read(path).join()).isEqualTo(content); + + op.delete(path).join(); + assertThatThrownBy(() -> op.stat(path).join()) .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.NotFound)); } - @Test - public void testAppendManyTimes() { - Map params = new HashMap<>(); - params.put("root", tempDir.toString()); - @Cleanup Operator op = new Operator("fs", params); + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getOperators") + public void testAppend(Operator op) { + assumeTrue(op != null); + + Capability cap = op.info().fullCapability; + if (!cap.write || !cap.writeCanAppend || !cap.read) { + return; + } - String[] trunks = new String[] {"first trunk", "second trunk", "third trunk"}; + String path = UUID.randomUUID().toString(); + byte[][] trunks = new byte[][] {Utils.generateBytes(), Utils.generateBytes(), Utils.generateBytes()}; for (int i = 0; i < trunks.length; i++) { - op.append("testAppendManyTimes", trunks[i]).join(); - String expected = Arrays.stream(trunks).limit(i + 1).collect(Collectors.joining()); - assertThat(op.read("testAppendManyTimes").join()).isEqualTo(expected.getBytes(StandardCharsets.UTF_8)); + op.append(path, trunks[i]).join(); + + byte[] expected = Arrays.stream(trunks).limit(i + 1).reduce(new byte[0], (arr1, arr2) -> { + byte[] result = new byte[arr1.length + arr2.length]; + System.arraycopy(arr1, 0, result, 0, arr1.length); + System.arraycopy(arr2, 0, result, arr1.length, arr2.length); + return result; + }); + + assertThat(op.read(path).join()).isEqualTo(expected); } // write overwrite existing content - op.write("testAppendManyTimes", "new attempt").join(); - assertThat(op.read("testAppendManyTimes").join()).isEqualTo("new attempt".getBytes(StandardCharsets.UTF_8)); + byte[] newAttempt = Utils.generateBytes(); + op.write(path, newAttempt).join(); + assertThat(op.read(path).join()).isEqualTo(newAttempt); for (int i = 0; i < trunks.length; i++) { - op.append("testAppendManyTimes", trunks[i]).join(); - String expected = Arrays.stream(trunks).limit(i + 1).collect(Collectors.joining()); - assertThat(op.read("testAppendManyTimes").join()) - .isEqualTo(("new attempt" + expected).getBytes(StandardCharsets.UTF_8)); + op.append(path, trunks[i]).join(); + + byte[] expected = Stream.concat( + Stream.of(newAttempt), Arrays.stream(trunks).limit(i + 1)) + .reduce(new byte[0], (arr1, arr2) -> { + byte[] result = new byte[arr1.length + arr2.length]; + System.arraycopy(arr1, 0, result, 0, arr1.length); + System.arraycopy(arr2, 0, result, arr1.length, arr2.length); + return result; + }); + + assertThat(op.read(path).join()).isEqualTo(expected); } } } diff --git a/bindings/java/src/test/java/org/apache/opendal/RedisServiceTest.java b/bindings/java/src/test/java/org/apache/opendal/RedisServiceTest.java deleted file mode 100644 index c13fbb21285b..000000000000 --- a/bindings/java/src/test/java/org/apache/opendal/RedisServiceTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.opendal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import lombok.Cleanup; -import org.apache.opendal.condition.OpenDALExceptionCondition; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -@Testcontainers -@DisabledIfEnvironmentVariable(named = "NO_DOCKER", matches = "true") -public class RedisServiceTest { - @Container - private final GenericContainer redisContainer = new GenericContainer<>("redis:7.2.1").withExposedPorts(6379); - - @Test - public void testAccessRedisService() { - assertThat(redisContainer.isRunning()).isTrue(); - - final Map params = new HashMap<>(); - params.put("root", "/tmp"); - params.put("endpoint", "tcp://127.0.0.1:" + redisContainer.getMappedPort(6379)); - @Cleanup final Operator op = new Operator("Redis", params); - - op.write("testAccessRedisService", "Odin").join(); - assertThat(op.read("testAccessRedisService").join()).isEqualTo("Odin".getBytes(StandardCharsets.UTF_8)); - op.delete("testAccessRedisService").join(); - assertThatThrownBy(() -> op.stat("testAccessRedisService").join()) - .is(OpenDALExceptionCondition.ofAsync(OpenDALException.Code.NotFound)); - } - - @Test - public void testAccessRedisServiceBlocking() { - assertThat(redisContainer.isRunning()).isTrue(); - - final Map params = new HashMap<>(); - params.put("root", "/tmp"); - params.put("endpoint", "tcp://127.0.0.1:" + redisContainer.getMappedPort(6379)); - @Cleanup final BlockingOperator op = new BlockingOperator("Redis", params); - - op.write("testAccessRedisServiceBlocking", "Odin"); - assertThat(op.read("testAccessRedisServiceBlocking")).isEqualTo("Odin".getBytes(StandardCharsets.UTF_8)); - op.delete("testAccessRedisServiceBlocking"); - assertThatExceptionOfType(OpenDALException.class) - .isThrownBy(() -> op.stat("testAccessRedisServiceBlocking")) - .extracting(OpenDALException::getCode) - .isEqualTo(OpenDALException.Code.NotFound); - } -} diff --git a/bindings/java/src/test/java/org/apache/opendal/StepsTest.java b/bindings/java/src/test/java/org/apache/opendal/StepsTest.java deleted file mode 100644 index dd5b8e46bcd6..000000000000 --- a/bindings/java/src/test/java/org/apache/opendal/StepsTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.opendal; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import io.cucumber.java.en.Given; -import io.cucumber.java.en.Then; -import io.cucumber.java.en.When; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import lombok.Cleanup; - -public class StepsTest { - BlockingOperator op; - - @Given("A new OpenDAL Blocking Operator") - public void a_new_open_dal_blocking_operator() { - Map params = new HashMap<>(); - params.put("root", "/tmp"); - op = new BlockingOperator("Memory", params); - } - - @When("Blocking write path {string} with content {string}") - public void blocking_write_path_test_with_content_hello_world(String path, String content) { - op.write(path, content); - } - - @Then("The blocking file {string} should exist") - public void the_blocking_file_test_should_exist(String path) { - @Cleanup Metadata metadata = op.stat(path); - assertNotNull(metadata); - } - - @Then("The blocking file {string} entry mode must be file") - public void the_blocking_file_test_entry_mode_must_be_file(String path) { - @Cleanup Metadata metadata = op.stat(path); - assertTrue(metadata.isFile()); - } - - @Then("The blocking file {string} content length must be {int}") - public void the_blocking_file_test_content_length_must_be_13(String path, int length) { - @Cleanup Metadata metadata = op.stat(path); - assertEquals(metadata.getContentLength(), length); - } - - @Then("The blocking file {string} must have content {string}") - public void the_blocking_file_test_must_have_content_hello_world(String path, String content) { - byte[] readContent = op.read(path); - assertThat(readContent).isEqualTo(content.getBytes(StandardCharsets.UTF_8)); - } -} diff --git a/bindings/java/src/test/java/org/apache/opendal/utils/Utils.java b/bindings/java/src/test/java/org/apache/opendal/utils/Utils.java new file mode 100644 index 000000000000..0121002d4973 --- /dev/null +++ b/bindings/java/src/test/java/org/apache/opendal/utils/Utils.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.opendal.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Properties; +import java.util.Random; +import java.util.UUID; +import java.util.stream.Collectors; +import org.apache.opendal.BlockingOperator; +import org.apache.opendal.Operator; + +public class Utils { + + public static final String ENV_NAME = ".env"; + public static final String CONF_PREFIX = "opendal_"; + public static final String CONF_TURN_ON_TEST = "test"; + public static final String CONF_ROOT = "root"; + public static final String CONF_RANDOM_ROOT_FLAG = "OPENDAL_DISABLE_RANDOM_ROOT"; + + /** + * Initializes the Service with the given schema. + * + * @param schema the schema to initialize the Operator service + * @return If `opendal_{schema}_test` is on, construct a new Operator with given root. + * Else, returns a `Empty` to represent no valid config for operator. + */ + public static Optional init(String schema) { + Map conf = readEnv(schema); + + final String turnOnTest = conf.get(CONF_TURN_ON_TEST); + if (!isTurnOn(turnOnTest)) { + return Optional.empty(); + } + Operator op = new Operator(schema, conf); + return Optional.of(op); + } + + /** + * Initializes a blocking operator using the provided schema. + * + * @param schema the schema to be used for initializing the blocking operator + * @return If `opendal_{schema}_test` is on, construct a new BlockingOperator with given root. + * Else, returns a `Empty` to represent no valid config for operator. + */ + public static Optional initBlockingOp(String schema) { + Map conf = readEnv(schema); + + final String turnOnTest = conf.get(CONF_TURN_ON_TEST); + if (!isTurnOn(turnOnTest)) { + return Optional.empty(); + } + BlockingOperator op = new BlockingOperator(schema, conf); + return Optional.of(op); + } + + /** + * Reads the environment variables and system properties and returns a map + * containing the configuration settings for the given schema. + * + * @param schema the schema for which to retrieve the configuration settings + * @return a map containing the configuration settings + */ + private static Map readEnv(String schema) { + final Properties properties = new Properties(); + + String projectRoot = System.getProperty("user.dir"); + + projectRoot = Optional.ofNullable(Paths.get(projectRoot)) + .map(Path::getParent) + .map(Path::getParent) + .map(Path::toString) + .orElse(projectRoot); + + try (BufferedReader reader = + new BufferedReader(new FileReader(projectRoot.toString() + File.separator + ENV_NAME))) { + properties.load(reader); + } catch (Exception ignore) { + } + for (Map.Entry entry : System.getenv().entrySet()) { + properties.setProperty(entry.getKey(), entry.getValue()); + } + + final String confPrefix = (CONF_PREFIX + schema).toLowerCase(); + final Map conf = properties.entrySet().stream() + .filter(Objects::nonNull) + .filter(entry -> Optional.ofNullable(entry.getKey()) + .map(Object::toString) + .orElse("") + .toLowerCase() + .startsWith(confPrefix)) + .collect(Collectors.toMap( + entry -> { + String key = entry.getKey().toString().toLowerCase(); + return key.replace(confPrefix + "_", ""); + }, + entry -> Optional.ofNullable(entry.getValue()) + .map(Object::toString) + .orElse(""), + (existing, replacement) -> existing)); + + if (!Boolean.parseBoolean(properties.getProperty(CONF_RANDOM_ROOT_FLAG))) { + String root = conf.getOrDefault(CONF_ROOT, File.separator); + if (!root.endsWith(File.separator)) { + root = root + File.separator; + } + root = root + UUID.randomUUID() + File.separator; + conf.put(CONF_ROOT, root); + } + return conf; + } + + /** + * Determines if the given value is turn on. + * + * @param val the value to be checked + * @return true if the value is "on" or "true", false otherwise + */ + public static boolean isTurnOn(String val) { + return "on".equalsIgnoreCase(val) || "true".equalsIgnoreCase(val); + } + + /** + * Generates a byte array of random content. + * + * @return the generated byte array + */ + public static byte[] generateBytes() { + Random random = new Random(); + + int size = random.nextInt(4 * 1024 * 1024) + 1; + byte[] content = new byte[size]; + random.nextBytes(content); + + return content; + } +}