From e951302e35815ba59651e4ef0cd1dae0192f5686 Mon Sep 17 00:00:00 2001 From: "Paul J. Davis" Date: Thu, 24 Aug 2023 12:05:37 -0500 Subject: [PATCH] Tests for tiledb_handle_load_enumerations_request Making these a separate PR that will be merged after the development branch of TileDB-Cloud-REST. If we included this in the same PR as adding the implementation we'd fail all the REST CI checks. I am open to suggestions on how to make this less terrible. --- test/CMakeLists.txt | 1 + test/src/unit-rest-enumerations.cc | 235 +++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 test/src/unit-rest-enumerations.cc diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8dd294ed847a..f9ecbc6f7bd1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -262,6 +262,7 @@ endif() if (TILEDB_TESTS_ENABLE_REST) list(APPEND TILEDB_UNIT_TEST_SOURCES src/unit-capi-rest-dense_array.cc + src/unit-rest-enumerations.cc ) endif() diff --git a/test/src/unit-rest-enumerations.cc b/test/src/unit-rest-enumerations.cc new file mode 100644 index 000000000000..c7f13c01b194 --- /dev/null +++ b/test/src/unit-rest-enumerations.cc @@ -0,0 +1,235 @@ +/** + * @file unit-rest-enumerations.cc + * + * @section LICENSE + * + * The MIT License + * + * @copyright Copyright (c) 2023 TileDB Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @section DESCRIPTION + * + * Tests serialization of Enumerations via a REST server. + */ + +#include "test/support/src/vfs_helpers.h" +#include "test/support/tdb_catch.h" +#include "tiledb/api/c_api/enumeration/enumeration_api_internal.h" +#include "tiledb/sm/array_schema/array_schema.h" +#include "tiledb/sm/c_api/tiledb_struct_def.h" +#include "tiledb/sm/cpp_api/tiledb" +#include "tiledb/sm/cpp_api/tiledb_experimental" + +using namespace tiledb; + +struct RESTEnumerationFx { + RESTEnumerationFx(); + ~RESTEnumerationFx(); + + void create_array(const std::string& array_name); + void rm_array(); + + std::string bucket_; + std::string uri_; + Config cfg_; + Context ctx_; + VFS vfs_; +}; + +TEST_CASE_METHOD( + RESTEnumerationFx, + "Create array test", + "[rest][enumeration][create-array]") { + create_array("simple-array-create"); +} + +TEST_CASE_METHOD( + RESTEnumerationFx, + "Simple enumeration query", + "[rest][enumeration][simple-query]") { + create_array("simple-query"); + + Array array(ctx_, uri_, TILEDB_READ); + Subarray subarray(ctx_, array); + subarray.set_subarray({1, 5}); + + QueryCondition qc(ctx_); + qc.init("attr1", "wilma", 5, TILEDB_EQ); + + std::vector attr1_read(5); + std::vector attr2_read(5); + + Query query(ctx_, array); + query.set_subarray(subarray) + .set_condition(qc) + .set_data_buffer("attr1", attr1_read) + .set_data_buffer("attr2", attr2_read); + + REQUIRE(query.submit() == Query::Status::COMPLETE); + REQUIRE(attr1_read[1] == 1); + REQUIRE(attr1_read[3] == 1); +} + +TEST_CASE_METHOD( + RESTEnumerationFx, + "Get enumeration", + "[rest][enumeration][get-enumeration]") { + create_array("get-enumeration"); + + Array array(ctx_, uri_, TILEDB_READ); + auto enmr = ArrayExperimental::get_enumeration(ctx_, array, "my_enum"); + + std::vector expected = {"fred", "wilma", "barney", "pebbles"}; + REQUIRE(enmr.as_vector() == expected); +} + +TEST_CASE_METHOD( + RESTEnumerationFx, + "Get previously loaded enumeration", + "[rest][enumeration][get-enumeration]") { + create_array("get-enumeration"); + + Array array(ctx_, uri_, TILEDB_READ); + auto enmr1 = ArrayExperimental::get_enumeration(ctx_, array, "my_enum"); + auto enmr2 = ArrayExperimental::get_enumeration(ctx_, array, "my_enum"); + + REQUIRE(enmr1.ptr()->copy() == enmr2.ptr()->copy()); + + std::vector expected = {"fred", "wilma", "barney", "pebbles"}; + REQUIRE(enmr2.as_vector() == expected); +} + +TEST_CASE_METHOD( + RESTEnumerationFx, + "Enumeration Extension", + "[rest][enumeration][extension]") { + create_array("get-enumeration"); + + Array old_array(ctx_, uri_, TILEDB_READ); + auto old_enmr = ArrayExperimental::get_enumeration(ctx_, old_array, "fruit"); + + std::vector fruit = { + "apple", "blueberry", "cherry", "durian", "elderberry"}; + auto new_enmr = old_enmr.extend(fruit); + + ArraySchemaEvolution ase(ctx_); + ase.extend_enumeration(new_enmr); + ase.array_evolve(uri_); + + Array new_array(ctx_, uri_, TILEDB_READ); + auto enmr = ArrayExperimental::get_enumeration(ctx_, new_array, "fruit"); + REQUIRE(enmr.as_vector() == fruit); +} + +Config& setup_config(Config& cfg) { + cfg["vfs.s3.endpoint_override"] = "localhost:9999"; + cfg["vfs.s3.scheme"] = "https"; + cfg["vfs.s3.use_virtual_addressing"] = "false"; + cfg["ssl.verify"] = "false"; + return cfg; +} + +RESTEnumerationFx::RESTEnumerationFx() + : bucket_("s3://enumeration-tests") + , ctx_(setup_config(cfg_)) + , vfs_(ctx_) { + if (!vfs_.is_bucket(bucket_)) { + vfs_.create_bucket(bucket_); + } +} + +RESTEnumerationFx::~RESTEnumerationFx() { + rm_array(); + if (vfs_.is_bucket(bucket_)) { + vfs_.remove_bucket(bucket_); + } +} + +void RESTEnumerationFx::create_array(const std::string& array_name) { + uri_ = "tiledb://unit/" + bucket_ + "/" + array_name; + + // Ensure that no array exists at uri_ + rm_array(); + + // Create a simple array for testing. This ends up with just five elements in + // the array. dim is an int32_t dimension, attr1 is an enumeration with string + // values and int32_t attribute values. attr2 is a float attribute. + // + // The array data is summarized as below, however, pay attention to the fact + // that attr1 is storing integral index values instead of the raw string data. + // + // dim = {1, 2, 3, 4, 5} + // attr1 = {"fred", "wilma", "barney", "wilma", "fred"} + // attr2 = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f} + ArraySchema schema(ctx_, TILEDB_DENSE); + + auto dim = Dimension::create(ctx_, "dim", {{-100, 100}}); + auto dom = Domain(ctx_); + dom.add_dimension(dim); + schema.set_domain(dom); + + // The list of string values in the attr1 enumeration + std::vector values = {"fred", "wilma", "barney", "pebbles"}; + auto enmr = Enumeration::create(ctx_, "my_enum", values); + ArraySchemaExperimental::add_enumeration(ctx_, schema, enmr); + + auto attr1 = Attribute::create(ctx_, "attr1"); + AttributeExperimental::set_enumeration_name(ctx_, attr1, "my_enum"); + schema.add_attribute(attr1); + + auto attr2 = Attribute::create(ctx_, "attr2"); + schema.add_attribute(attr2); + + auto fruit = Enumeration::create_empty( + ctx_, "fruit", TILEDB_STRING_ASCII, TILEDB_VAR_NUM); + ArraySchemaExperimental::add_enumeration(ctx_, schema, fruit); + + auto attr3 = Attribute::create(ctx_, "attr3"); + AttributeExperimental::set_enumeration_name(ctx_, attr3, "fruit"); + schema.add_attribute(attr3); + + Array::create(uri_, schema); + + // Attribute data + std::vector attr1_values = {0, 1, 2, 1, 0}; + std::vector attr2_values = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f}; + std::vector attr3_values = {0, 1, 2, 3, 4}; + + Array array(ctx_, uri_, TILEDB_WRITE); + Subarray subarray(ctx_, array); + subarray.set_subarray({1, 5}); + Query query(ctx_, array); + query.set_subarray(subarray) + .set_layout(TILEDB_ROW_MAJOR) + .set_data_buffer("attr1", attr1_values) + .set_data_buffer("attr2", attr2_values) + .set_data_buffer("attr3", attr3_values); + CHECK_NOTHROW(query.submit()); + query.finalize(); + array.close(); +} + +void RESTEnumerationFx::rm_array() { + auto obj = Object::object(ctx_, uri_); + if (obj.type() == Object::Type::Array) { + Array::delete_array(ctx_, uri_); + } +}