Skip to content
Snippets Groups Projects
Commit ca07e4d2 authored by florian's avatar florian
Browse files

Merge branch 'f-extended-c' into f-remove-unique-result

parents f38dbd12 ec2eb3eb
No related branches found
No related tags found
4 merge requests!12F consolidation,!10API: remove UniqueResult, lower-case at, size for ResultSet,!9Draft: API: remove UniqueResult, lower-case at, size for ResultSet,!8ENH: Add retrieval and queries to Extern C interface
This commit is part of merge request !10. Comments created here will be created in the context of that merge request.
......@@ -48,7 +48,7 @@ as compiler. We use [cmake](https://cmake.org/download/) as build tool.
and build the project. (You can open Tools/Command Line/Developer Command
Prompt and execute `msbuild libcaosdb.sln /property:Configuration=Release`)
### Creating a Local Conan Build ##
### Creating a local Conan package ##
Building and installing libcaosdb with Conan is just a single command:
`conan create . -s "compiler.libcxx=libstdc++11"`
......
......@@ -24,6 +24,21 @@
C API
=====
.. note::
When working with libcaosdb's C API keep the following in
mind. Delete all objects (transactions, entities, properties,
parents, ...) that you created using a `caosdb_..._create_...`
function and only those.
The underlying reason is that all C++ objects are realized in the
Extern C interface as mutable structs containing a void pointer to
the actuall C++ object which is not filled when initializing the
struct but after calling a create function instead. If the C++
object wasn't created using a create function, e.g., the parent
object when getting a parent of an entity, it is owned by another
object and deleted together with that object.
.. toctree::
:glob:
......
......@@ -29,10 +29,14 @@
#ifndef CAOSDB_ENTITY_H
#define CAOSDB_ENTITY_H
#include <string> // for string
#include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message
#include "caosdb/entity/v1alpha1/main.pb.h" // for ProtoEntity, ProtoParent...
#include "caosdb/message_code.h" // for get_message_code, Messag...
#include "google/protobuf/util/json_util.h" // for MessageToJsonString, Jso...
#include <google/protobuf/util/json_util.h> // for MessageToJsonString, Jso...
#include <google/protobuf/message.h> // for RepeatedPtrField, Message
#include <iterator> // for iterator, output_iterato...
#include <map> // for map
#include <stdexcept> // for out_of_range
#include <string> // for string
namespace caosdb::entity {
using caosdb::entity::v1alpha1::IdResponse;
......@@ -40,18 +44,202 @@ using ProtoParent = caosdb::entity::v1alpha1::Parent;
using ProtoProperty = caosdb::entity::v1alpha1::Property;
using ProtoEntity = caosdb::entity::v1alpha1::Entity;
using ProtoMessage = caosdb::entity::v1alpha1::Message;
using ::google::protobuf::RepeatedPtrField;
static const std::string logger_name = "caosdb::entity";
/**
* Messages convey information about the state and result of transactions.
* Abstract base class for Messages, Properties and Parents container classes.
*
* This is a list-like class.
*/
template <typename T, typename P> class RepeatedPtrFieldWrapper {
class iterator;
public:
/**
* Return the current size of the container.
*/
[[nodiscard]] inline auto Size() const -> int { return wrapped->size(); }
/**
* Return a const reference to the element at the given index.
*/
[[nodiscard]] inline auto At(int index) const -> const T & {
return *mutable_at(index);
}
/**
* Return a mutable pointer to the element at the given index.
*/
[[nodiscard]] inline auto mutable_at(int index) const -> T * {
if (index >= Size() || index < 0) {
throw std::out_of_range("Container has size " + std::to_string(Size()));
}
if (cache.count(index) == 0) {
cache.emplace(index, T(&(wrapped->at(index))));
}
return &(cache.at(index));
}
/**
* Return iterator positioned at the beginning of the list.
*/
auto begin() -> iterator;
/**
* Return iterator positioned at the end of the list.
*/
auto end() -> iterator;
/**
* Return constant iterator positioned at the beginning of the list.
*/
auto begin() const -> const iterator;
/**
* Return constant iterator positioned at the end of the list.
*/
auto end() const -> const iterator;
friend class Entity;
virtual ~RepeatedPtrFieldWrapper(){};
protected:
RepeatedPtrFieldWrapper(){};
explicit inline RepeatedPtrFieldWrapper(
::google::protobuf::RepeatedPtrField<P> *wrapped)
: wrapped(wrapped){};
/**
* Append an element. This adds the element to the end of the wrapped list
* and increases the size by one.
*/
inline auto Append(const T &element) -> void {
auto *destination = this->wrapped->Add();
destination->Swap(element.wrapped);
// Clear the originally wrapped object and return it to the Arena
element.wrapped->Clear();
// set the pointer to the new object which is owned by the RepeatedPtrField
element.wrapped = destination;
}
/**
* Remove the element at the given index.
*/
inline auto remove(int index) -> void {
this->wrapped->DeleteSubrange(index, 1);
if (cache.count(index) > 0) {
cache.erase(index);
}
// shift all indices in the cache above index (such that the values do not
// get deleted/copied because this could destroy pointers (c-interface).
for (int i = index + 1; i < Size(); i++) {
if (cache.count(i) > 0) {
auto handle = cache.extract(i);
handle.key()--;
cache.insert(std::move(handle));
}
}
}
::google::protobuf::RepeatedPtrField<P> *wrapped;
mutable std::map<int, T> cache;
private:
class iterator : public std::iterator<std::output_iterator_tag, T> {
public:
explicit iterator(const RepeatedPtrFieldWrapper<T, P> *instance,
int index = 0);
auto operator*() const -> T &;
auto operator++() -> iterator &;
auto operator++(int) -> iterator;
auto operator!=(const iterator &rhs) const -> bool;
private:
int current_index = 0;
const RepeatedPtrFieldWrapper<T, P> *instance;
};
};
template <class T, class P>
RepeatedPtrFieldWrapper<T, P>::iterator::iterator(
const RepeatedPtrFieldWrapper<T, P> *instance, int index)
: current_index(index), instance(instance) {}
template <typename T, typename P>
auto RepeatedPtrFieldWrapper<T, P>::iterator::operator*() const -> T & {
return *(this->instance->mutable_at(current_index));
}
template <typename T, typename P>
auto RepeatedPtrFieldWrapper<T, P>::iterator::operator++()
-> RepeatedPtrFieldWrapper<T, P>::iterator & {
current_index++;
return *this;
}
template <typename T, typename P>
auto RepeatedPtrFieldWrapper<T, P>::iterator::operator++(int)
-> RepeatedPtrFieldWrapper<T, P>::iterator {
iterator tmp(*this);
operator++();
return tmp;
}
template <typename T, typename P>
auto RepeatedPtrFieldWrapper<T, P>::iterator::operator!=(
const iterator &rhs) const -> bool {
return this->current_index != rhs.current_index;
}
template <typename T, typename P>
auto RepeatedPtrFieldWrapper<T, P>::begin()
-> RepeatedPtrFieldWrapper<T, P>::iterator {
return RepeatedPtrFieldWrapper<T, P>::iterator(this, 0);
}
template <typename T, typename P>
auto RepeatedPtrFieldWrapper<T, P>::end()
-> RepeatedPtrFieldWrapper<T, P>::iterator {
return RepeatedPtrFieldWrapper<T, P>::iterator(this, Size());
}
template <typename T, typename P>
auto RepeatedPtrFieldWrapper<T, P>::begin() const
-> const RepeatedPtrFieldWrapper<T, P>::iterator {
return RepeatedPtrFieldWrapper<T, P>::iterator(this, 0);
}
template <typename T, typename P>
auto RepeatedPtrFieldWrapper<T, P>::end() const
-> const RepeatedPtrFieldWrapper<T, P>::iterator {
return RepeatedPtrFieldWrapper<T, P>::iterator(this, Size());
}
/**
* Messages convey information about the state of entities and result of
* transactions.
*
* A Message object can be thought of as kinf of a generalized error object in
* other frameworks. Please have a look at MessageCodes for more details.
*/
class Message {
public:
/**
* Get the code of this message.
*
* The message code is a unique identifier of the type of message and is
* intended to make it easiert to identify messages in a machine-readable
* way.
*/
[[nodiscard]] inline auto GetCode() const -> MessageCode {
return get_message_code(wrapped->code());
}
/**
* Get the description of this message.
*
* The description is intended for a human reader.
*/
[[nodiscard]] inline auto GetDescription() const -> std::string {
return wrapped->description();
}
......@@ -62,6 +250,7 @@ public:
// friend class Parent;
// friend class Property;
friend class Messages;
friend class RepeatedPtrFieldWrapper<Message, ProtoMessage>;
private:
explicit inline Message(ProtoMessage *wrapped) : wrapped(wrapped){};
......@@ -72,12 +261,9 @@ private:
/**
* Container for Messages.
*/
class Messages {
class Messages : public RepeatedPtrFieldWrapper<Message, ProtoMessage> {
public:
[[nodiscard]] inline auto Size() const -> int { return wrapped->size(); }
[[nodiscard]] inline auto At(int index) const -> const Message {
return Message(&(wrapped->at(index)));
}
~Messages();
friend class Entity;
// TODO(fspreck) Same here.
......@@ -85,9 +271,7 @@ public:
// friend class Property;
private:
inline Messages() : wrapped(nullptr){};
::google::protobuf::RepeatedPtrField<ProtoMessage> *wrapped;
inline Messages() : RepeatedPtrFieldWrapper(){};
};
/**
......@@ -166,6 +350,7 @@ public:
friend class Entity;
friend class Parents;
friend class RepeatedPtrFieldWrapper<Parent, ProtoParent>;
private:
/**
......@@ -194,40 +379,17 @@ private:
*
* Should only be instantiated and write-accessed by the owning entity.
*/
class Parents {
class Parents : public RepeatedPtrFieldWrapper<Parent, ProtoParent> {
public:
/**
* Return the current size of the parent container.
*
* That is also the number of parents the owning entity currently has.
*/
[[nodiscard]] inline auto Size() const -> int { return wrapped->size(); }
/**
* Return the parent at the given index.
*/
[[nodiscard]] inline auto At(int index) const -> const Parent {
return Parent(&(wrapped->at(index)));
}
~Parents() = default;
friend class Entity;
private:
inline Parents(){};
inline Parents() : RepeatedPtrFieldWrapper(){};
explicit inline Parents(
::google::protobuf::RepeatedPtrField<ProtoParent> *wrapped)
: wrapped(wrapped){};
/**
* Append a parent.
*
* This increases the Size() by one.
*/
auto Append(const Parent &parent) -> void;
/**
* The collection of parent messages which serves as a backend for this
* class.
*/
::google::protobuf::RepeatedPtrField<ProtoParent> *wrapped;
::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Parent>
*wrapped)
: RepeatedPtrFieldWrapper(wrapped){};
};
/**
......@@ -320,6 +482,7 @@ public:
friend class Entity;
friend class Properties;
friend class RepeatedPtrFieldWrapper<Property, ProtoProperty>;
private:
static auto CreateProtoProperty() -> ProtoProperty *;
......@@ -332,37 +495,20 @@ private:
*
* Should only be instantiated and write-accessed by the owning entity.
*/
class Properties {
class Properties
: public RepeatedPtrFieldWrapper<Property,
caosdb::entity::v1alpha1::Property> {
public:
/**
* Return the current size of the properties container.
*
* This is also the number of properties the owningn entity currently has.
*/
[[nodiscard]] inline auto Size() const -> int { return wrapped->size(); }
/**
* Return the property at the given index.
*/
[[nodiscard]] auto At(int index) const -> const Property {
return Property(&(wrapped->at(index)));
}
~Properties() = default;
friend class Entity;
private:
inline Properties(){};
explicit inline Properties(
::google::protobuf::RepeatedPtrField<ProtoProperty> *wrapped)
: wrapped(wrapped){};
/**
* Append a property
*
* This increases the Size() by one.
*/
auto Append(const Property &property) -> void;
::google::protobuf::RepeatedPtrField<ProtoProperty> *wrapped;
::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Property>
*wrapped)
: RepeatedPtrFieldWrapper<Property, caosdb::entity::v1alpha1::Property>(
wrapped){};
};
/**
......@@ -448,9 +594,12 @@ public:
auto SetUnit(const std::string &unit) -> void;
// Currently no references or lists.
auto SetDatatype(const std::string &datatype) -> void;
auto AppendProperty(const Property &property) -> void;
auto RemoveProperty(int index) -> void;
auto AppendParent(const Parent &parent) -> void;
auto RemoveParent(int index) -> void;
/**
* Copy all of this entity's features to the target ProtoEntity.
*/
......
......@@ -52,6 +52,7 @@ enum StatusCode {
TRANSACTION_TYPE_ERROR = 26,
UNSUPPORTED_FEATURE = 27,
ORIGINAL_ENTITY_MISSING_ID = 28,
EXTERN_C_ASSIGNMENT_ERROR = 29,
};
auto get_status_description(int code) -> const std::string &;
......
/*
* This file is a part of the CaosDB Project.
* Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
* Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@indiscale.com>
* Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
......@@ -40,6 +61,7 @@ const char *caosdb_constants_COMPATIBLE_SERVER_VERSION_PRE_RELEASE();
*/
typedef struct {
void *wrapped_connection;
bool _deletable = false;
} caosdb_connection_connection;
/**
......@@ -51,6 +73,7 @@ typedef struct {
*/
typedef struct {
void *wrapped_connection_configuration;
bool _deletable = false;
} caosdb_connection_connection_configuration;
/**
......@@ -70,10 +93,12 @@ typedef struct {
typedef struct {
void *wrapped_certificate_provider;
bool _deletable = false;
} caosdb_connection_certificate_provider;
typedef struct {
void *wrapped_authenticator;
bool _deletable = false;
} caosdb_authentication_authenticator;
/**
......@@ -242,62 +267,77 @@ int caosdb_connection_connection_manager_get_connection(
// not sufficient yet.
typedef struct {
void *wrapped_transaction;
bool _deletable = false;
} caosdb_transaction_transaction;
/**
* Create a transaction on an existing connection.
*
* This transaction has to be deleted manually by
* caosdb_transaction_delete_transaction() later on.
*/
int caosdb_connection_connection_create_transaction(
caosdb_connection_connection *connection,
caosdb_transaction_transaction *out);
int caosdb_transaction_delete_transaction(
caosdb_transaction_transaction *transaction);
int caosdb_transaction_transaction_retrieve_by_id(
caosdb_transaction_transaction *transaction, const char *id);
int caosdb_transaction_transaction_retrieve_by_ids(
caosdb_transaction_transaction *transaction, const char *ids[], int length);
int caosdb_transaction_transaction_query(
caosdb_transaction_transaction *transaction, const char *query);
int caosdb_transaction_transaction_execute(
caosdb_transaction_transaction *transaction);
// TODO(fspreck) execute_asynchronously may be added as a separate
// function once we actually support asynchronous execution.
typedef struct {
void *wrapped_result_set;
bool _deletable = false;
} caosdb_transaction_result_set;
int caosdb_transaction_transaction_get_result_set(
caosdb_transaction_transaction *transaction,
caosdb_transaction_result_set *out);
int caosdb_transaction_transaction_get_count_result(
caosdb_transaction_transaction *transaction, long *out);
typedef struct {
void *wrapped_entity;
char **id;
char **role;
char **name;
char **description;
char **datatype;
char **unit;
char **value;
char **version_id;
bool _deletable = false;
} caosdb_entity_entity;
int caosdb_transaction_result_set_get_entity(
caosdb_transaction_result_set *result_set, caosdb_entity_entity *entity,
int index);
int caosdb_transaction_result_set_at(caosdb_transaction_result_set *result_set,
caosdb_entity_entity *entity, int index);
int caosdb_transaction_result_set_size(
caosdb_transaction_result_set *result_set, int *out);
typedef struct {
void *wrapped_property;
char **id;
char **name;
char **description;
char **datatype;
char **unit;
char **value;
bool _deletable = false;
} caosdb_entity_property;
typedef struct {
void *wrapped_parent;
char **id;
char **name;
char **description;
bool _deletable = false;
} caosdb_entity_parent;
typedef struct {
void *wrapped_message;
int *code;
char **description;
bool _deletable = false;
} caosdb_entity_message;
// GETTERS FOR COMPLEX OBJECTS
// GETTERS FOR EVERYTHING
int caosdb_entity_entity_get_id(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_role(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_name(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_description(caosdb_entity_entity *entity,
char *out);
int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_unit(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_value(caosdb_entity_entity *entity, char *out);
int caosdb_entity_entity_get_version_id(caosdb_entity_entity *entity,
char *out);
int caosdb_entity_entity_get_errors_size(caosdb_entity_entity *entity,
int *out);
int caosdb_entity_entity_get_error(caosdb_entity_entity *entity,
......@@ -318,6 +358,29 @@ int caosdb_entity_entity_get_parents_size(caosdb_entity_entity *entity,
int caosdb_entity_entity_get_parent(caosdb_entity_entity *entity,
caosdb_entity_parent *out, int index);
int caosdb_entity_property_get_id(caosdb_entity_property *property, char *out);
int caosdb_entity_property_get_name(caosdb_entity_property *property,
char *out);
int caosdb_entity_property_get_description(caosdb_entity_property *property,
char *out);
int caosdb_entity_property_get_importance(caosdb_entity_property *property,
char *out);
int caosdb_entity_property_get_datatype(caosdb_entity_property *property,
char *out);
int caosdb_entity_property_get_unit(caosdb_entity_property *property,
char *out);
int caosdb_entity_property_get_value(caosdb_entity_property *property,
char *out);
int caosdb_entity_parent_get_id(caosdb_entity_parent *parent, char *out);
int caosdb_entity_parent_get_name(caosdb_entity_parent *parent, char *out);
int caosdb_entity_parent_get_description(caosdb_entity_parent *parent,
char *out);
int caosdb_entity_message_get_code(caosdb_entity_message *message, int *out);
int caosdb_entity_message_get_description(caosdb_entity_message *message,
char *out);
// CONSTRUCTORS AND DESTRUCTORS
int caosdb_entity_create_entity(caosdb_entity_entity *out);
int caosdb_entity_delete_entity(caosdb_entity_entity *out);
......@@ -326,11 +389,43 @@ int caosdb_entity_delete_property(caosdb_entity_property *out);
int caosdb_entity_create_parent(caosdb_entity_parent *out);
int caosdb_entity_delete_parent(caosdb_entity_parent *out);
// SETTERS FOR COMPLEX OBJECTS
// SETTERS FOR EVERYTHING THAT MAY BE SET
int caosdb_entity_entity_set_role(caosdb_entity_entity *entity,
const char *role);
int caosdb_entity_entity_set_name(caosdb_entity_entity *entity,
const char *name);
int caosdb_entity_entity_set_description(caosdb_entity_entity *entity,
const char *description);
int caosdb_entity_entity_set_datatype(caosdb_entity_entity *entity,
const char *datatype);
int caosdb_entity_entity_set_unit(caosdb_entity_entity *entity,
const char *unit);
int caosdb_entity_entity_set_value(caosdb_entity_entity *entity,
const char *value);
int caosdb_entity_entity_append_parent(caosdb_entity_entity *entity,
caosdb_entity_parent *parent);
int caosdb_entity_entity_remove_parent(caosdb_entity_entity *entity, int index);
int caosdb_entity_entity_append_property(caosdb_entity_entity *entity,
caosdb_entity_property *property);
int caosdb_entity_entity_remove_property(caosdb_entity_entity *entity,
int index);
int caosdb_entity_property_set_id(caosdb_entity_property *property,
const char *id);
int caosdb_entity_property_set_name(caosdb_entity_property *property,
const char *name);
int caosdb_entity_property_set_datatype(caosdb_entity_property *property,
const char *datatype);
int caosdb_entity_property_set_importance(caosdb_entity_property *property,
const char *importance);
int caosdb_entity_property_set_unit(caosdb_entity_property *property,
const char *unit);
int caosdb_entity_property_set_value(caosdb_entity_property *property,
const char *value);
int caosdb_entity_parent_set_id(caosdb_entity_parent *parent, const char *id);
int caosdb_entity_parent_set_name(caosdb_entity_parent *parent,
const char *name);
#ifdef __cplusplus
}
......
......@@ -31,6 +31,8 @@ using ProtoProperty = caosdb::entity::v1alpha1::Property;
using ProtoEntity = caosdb::entity::v1alpha1::Entity;
using caosdb::utility::get_arena;
Messages::~Messages() = default;
Parent::Parent() : wrapped(Parent::CreateProtoParent()) {
// TODO(fspreck) Re-enable once we have decided how to attach
// messages to parents.
......@@ -47,10 +49,6 @@ auto Parent::SetName(const std::string &name) -> void {
this->wrapped->set_name(name);
}
auto Parent::GetDescription() const -> const std::string & {
return this->wrapped->description();
}
auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); }
[[nodiscard]] auto Parent::GetId() const -> const std::string & {
......@@ -61,15 +59,8 @@ auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); }
return this->wrapped->name();
}
auto Parents::Append(const Parent &parent) -> void {
auto *destination = this->wrapped->Add();
destination->Swap(parent.wrapped);
// Clear the originally wrapped object and return it to the Arena
parent.wrapped->Clear();
// set the pointer to the new object which is owned by the RepeatedPtrField
parent.wrapped = destination;
[[nodiscard]] auto Parent::GetDescription() const -> const std::string & {
return this->wrapped->description();
}
Property::Property() : wrapped(Property::CreateProtoProperty()) {}
......@@ -134,15 +125,6 @@ auto Property::SetDatatype(const std::string &datatype) -> void {
this->wrapped->set_datatype(datatype);
}
auto Properties::Append(const Property &property) -> void {
auto *destination = this->wrapped->Add();
destination->Swap(property.wrapped);
property.wrapped->Clear();
property.wrapped = destination;
}
[[nodiscard]] auto Entity::GetParents() const -> const Parents & {
return parents;
}
......@@ -151,6 +133,8 @@ auto Entity::AppendParent(const Parent &parent) -> void {
this->parents.Append(parent);
}
auto Entity::RemoveParent(int index) -> void { this->parents.remove(index); }
[[nodiscard]] auto Entity::GetProperties() const -> const Properties & {
return properties;
}
......@@ -159,6 +143,10 @@ auto Entity::AppendProperty(const Property &property) -> void {
this->properties.Append(property);
}
auto Entity::RemoveProperty(int index) -> void {
this->properties.remove(index);
}
auto Entity::CreateProtoEntity() -> ProtoEntity * {
return google::protobuf::Arena::CreateMessage<ProtoEntity>(get_arena());
}
......
......@@ -80,7 +80,10 @@ auto get_status_description(int code) -> const std::string & {
"an id. This is the case when you did not retrieve it before applying any "
"changes and instantiated the Entity class explicitely."},
{StatusCode::UNSUPPORTED_FEATURE,
"This feature is not available in the this client implementation."}};
"This feature is not available in the this client implementation."},
{StatusCode::EXTERN_C_ASSIGNMENT_ERROR,
"You tried to assign a new object to the wrapped void pointer. You have "
"to delete the old pointee first."}};
try {
return descriptions.at(code);
} catch (const std::out_of_range &exc) {
......
This diff is collapsed.
......@@ -21,12 +21,15 @@
*/
#include "caosdb/configuration.h"
#include "caosdb/status_code.h" // for StatusCode
#include "caosdb_test_utility.h" // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
#include "ccaosdb.h" // for caosdb_utility_get_env_var
#include <cstring> // for strcmp
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl
#include <gtest/gtest_pred_impl.h> // for Test, TestInfo, EXPECT_EQ, TEST
#include <string> // for allocator
#include <iostream>
#include <string> // for allocator
class test_ccaosdb : public ::testing::Test {
protected:
......@@ -61,3 +64,349 @@ TEST_F(test_ccaosdb, test_get_connection) {
"local-caosdb-admin");
EXPECT_TRUE(out.wrapped_connection);
}
TEST_F(test_ccaosdb, test_execute_transaction) {
caosdb_connection_connection connection;
caosdb_connection_connection_manager_get_connection(&connection,
"local-caosdb-admin");
caosdb_transaction_transaction transaction;
caosdb_connection_connection_create_transaction(&connection, &transaction);
EXPECT_TRUE(transaction.wrapped_transaction);
int return_code(
caosdb_transaction_transaction_retrieve_by_id(&transaction, "some_id"));
EXPECT_EQ(return_code, caosdb::StatusCode::GO_ON);
return_code = caosdb_transaction_transaction_execute(&transaction);
EXPECT_EQ(return_code, caosdb::StatusCode::CONNECTION_ERROR);
return_code = caosdb_transaction_delete_transaction(&transaction);
EXPECT_EQ(return_code, 0);
caosdb_transaction_transaction multi_transaction;
caosdb_connection_connection_create_transaction(&connection,
&multi_transaction);
// We explicitely want to define a C-style array here, so we disable
// linting
const char *ids[] = {"id1", "id2", "id3"}; // NOLINT
return_code =
caosdb_transaction_transaction_retrieve_by_ids(&multi_transaction, ids, 3);
EXPECT_EQ(return_code, caosdb::StatusCode::GO_ON);
return_code = caosdb_transaction_delete_transaction(&multi_transaction);
EXPECT_EQ(return_code, 0);
}
TEST_F(test_ccaosdb, test_multi_retrieve) {
std::cout << "Entering test_multi_retrieve ..." << std::endl;
caosdb_connection_connection connection;
caosdb_connection_connection_manager_get_connection(&connection,
"local-caosdb-admin");
std::cout << "Creating transaction" << std::endl;
caosdb_transaction_transaction multi_transaction;
caosdb_connection_connection_create_transaction(&connection,
&multi_transaction);
// We explicitely want to define a C-style array here, so we disable
// linting
const char *ids[] = {"id1", "id2", "id3"}; // NOLINT
std::cout << "Adding mutli retrieval ..." << std::endl;
int return_code(
caosdb_transaction_transaction_retrieve_by_ids(&multi_transaction, ids, 3));
EXPECT_EQ(return_code, caosdb::StatusCode::GO_ON);
std::cout << "Deleting transaction ..." << std::endl;
return_code = caosdb_transaction_delete_transaction(&multi_transaction);
EXPECT_EQ(return_code, 0);
}
TEST_F(test_ccaosdb, test_query) {
caosdb_connection_connection connection;
caosdb_connection_connection_manager_get_connection(&connection,
"local-caosdb-admin");
caosdb_transaction_transaction transaction;
caosdb_connection_connection_create_transaction(&connection, &transaction);
int return_code(caosdb_transaction_transaction_query(
&transaction, "FIND ENTITY WITH id=123"));
EXPECT_EQ(return_code, caosdb::StatusCode::GO_ON);
return_code = caosdb_transaction_delete_transaction(&transaction);
EXPECT_EQ(return_code, 0);
}
TEST_F(test_ccaosdb, test_entity) {
caosdb_entity_entity entity;
int return_code(caosdb_entity_create_entity(&entity));
EXPECT_EQ(return_code, 0);
// cannot be created again without deletion
return_code = caosdb_entity_create_entity(&entity);
EXPECT_EQ(return_code, caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR);
// deletion and re-creation is ok
return_code = caosdb_entity_delete_entity(&entity);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_create_entity(&entity);
EXPECT_EQ(return_code, 0);
// In-depth check for one pair of setter and getter, just compare
// the strings for the rest
return_code = caosdb_entity_entity_set_name(&entity, "length");
EXPECT_EQ(return_code, 0);
char out[255] = {"a"}; // NOLINT
return_code = caosdb_entity_entity_get_name(&entity, out);
EXPECT_EQ(return_code, 0);
EXPECT_EQ(strcmp(out, "length"), 0);
caosdb_entity_entity_set_role(&entity, "Property");
caosdb_entity_entity_get_role(&entity, out);
EXPECT_EQ(strcmp(out, "Property"), 0);
caosdb_entity_entity_set_description(&entity, "The length of an object");
caosdb_entity_entity_get_description(&entity, out);
EXPECT_EQ(strcmp(out, "The length of an object"), 0);
caosdb_entity_entity_set_datatype(&entity, "DOUBLE");
caosdb_entity_entity_get_datatype(&entity, out);
EXPECT_EQ(strcmp(out, "DOUBLE"), 0);
caosdb_entity_entity_set_unit(&entity, "m");
caosdb_entity_entity_get_unit(&entity, out);
EXPECT_EQ(strcmp(out, "m"), 0);
caosdb_entity_entity_set_value(&entity, "5.0");
caosdb_entity_entity_get_value(&entity, out);
EXPECT_EQ(strcmp(out, "5.0"), 0);
return_code = caosdb_entity_delete_entity(&entity);
EXPECT_EQ(return_code, 0);
}
TEST_F(test_ccaosdb, test_parent) {
caosdb_entity_parent parent;
int return_code(caosdb_entity_create_parent(&parent));
EXPECT_EQ(return_code, 0);
caosdb_entity_parent_set_id(&parent, "some_id");
caosdb_entity_parent_set_name(&parent, "some_name");
char out[255] = {"a"}; // NOLINT
caosdb_entity_parent_get_id(&parent, out);
EXPECT_EQ(strcmp(out, "some_id"), 0);
caosdb_entity_parent_get_name(&parent, out);
EXPECT_EQ(strcmp(out, "some_name"), 0);
return_code = caosdb_entity_delete_parent(&parent);
EXPECT_EQ(return_code, 0);
}
TEST_F(test_ccaosdb, test_property) {
caosdb_entity_property property;
int return_code(caosdb_entity_create_property(&property));
EXPECT_EQ(return_code, 0);
caosdb_entity_property_set_id(&property, "some_id");
caosdb_entity_property_set_name(&property, "some_name");
caosdb_entity_property_set_datatype(&property, "some_datatype");
caosdb_entity_property_set_importance(&property, "some_importance");
caosdb_entity_property_set_unit(&property, "some_unit");
caosdb_entity_property_set_value(&property, "some_value");
char out[255] = {"a"}; // NOLINT
caosdb_entity_property_get_id(&property, out);
EXPECT_EQ(strcmp(out, "some_id"), 0);
caosdb_entity_property_get_name(&property, out);
EXPECT_EQ(strcmp(out, "some_name"), 0);
caosdb_entity_property_get_datatype(&property, out);
EXPECT_EQ(strcmp(out, "some_datatype"), 0);
caosdb_entity_property_get_importance(&property, out);
EXPECT_EQ(strcmp(out, "some_importance"), 0);
caosdb_entity_property_get_unit(&property, out);
EXPECT_EQ(strcmp(out, "some_unit"), 0);
caosdb_entity_property_get_value(&property, out);
EXPECT_EQ(strcmp(out, "some_value"), 0);
return_code = caosdb_entity_delete_property(&property);
EXPECT_EQ(return_code, 0);
}
TEST_F(test_ccaosdb, test_entity_with_parent_and_property) {
std::cout << "Creating objects ... " << std::endl;
caosdb_entity_parent input_parent;
int return_code(caosdb_entity_create_parent(&input_parent));
EXPECT_EQ(return_code, 0);
caosdb_entity_parent_set_id(&input_parent, "parent_id");
caosdb_entity_parent_set_name(&input_parent, "parent_name");
caosdb_entity_property input_property;
return_code = caosdb_entity_create_property(&input_property);
EXPECT_EQ(return_code, 0);
caosdb_entity_property_set_id(&input_property, "property_id");
caosdb_entity_property_set_name(&input_property, "property_name");
caosdb_entity_property_set_datatype(&input_property, "property_datatype");
caosdb_entity_property_set_value(&input_property, "property_value");
caosdb_entity_entity entity;
return_code = caosdb_entity_create_entity(&entity);
EXPECT_EQ(return_code, 0);
std::cout << "Appending parent and property ..." << std::endl;
return_code = caosdb_entity_entity_append_parent(&entity, &input_parent);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_entity_append_property(&entity, &input_property);
EXPECT_EQ(return_code, 0);
std::cout << "Counting parents and properties ..." << std::endl;
int count[] = {0}; // NOLINT
return_code = caosdb_entity_entity_get_parents_size(&entity, count);
EXPECT_EQ(return_code, 0);
EXPECT_EQ(*count, 1);
return_code = caosdb_entity_entity_get_properties_size(&entity, count);
EXPECT_EQ(return_code, 0);
EXPECT_EQ(*count, 1);
char in[255] = {"a"}; // NOLINT
char out[255] = {"b"}; // NOLINT
std::cout << "Comparing ..." << std::endl;
// cannot assign an already assigned property
return_code = caosdb_entity_entity_get_property(&entity, &input_property, 0);
EXPECT_EQ(return_code, caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR);
caosdb_entity_property output_property;
return_code = caosdb_entity_entity_get_property(&entity, &output_property, 0);
std::cout << "Got output property." << std::endl;
EXPECT_EQ(return_code, 0);
caosdb_entity_property_get_id(&input_property, in);
std::cout << "Got input id." << std::endl;
caosdb_entity_property_get_id(&output_property, out);
std::cout << "Got output id." << std::endl;
EXPECT_EQ(strcmp(in, out), 0);
caosdb_entity_property_get_name(&input_property, in);
caosdb_entity_property_get_name(&output_property, out);
EXPECT_EQ(strcmp(in, out), 0);
caosdb_entity_property_get_datatype(&input_property, in);
caosdb_entity_property_get_datatype(&output_property, out);
EXPECT_EQ(strcmp(in, out), 0);
caosdb_entity_property_get_value(&input_property, in);
caosdb_entity_property_get_value(&output_property, out);
EXPECT_EQ(strcmp(in, out), 0);
std::cout << "Comparing parent..." << std::endl;
caosdb_entity_parent output_parent;
return_code = caosdb_entity_entity_get_parent(&entity, &output_parent, 0);
std::cout << "Got output parent." << std::endl;
EXPECT_EQ(return_code, 0);
caosdb_entity_parent_get_id(&input_parent, in);
caosdb_entity_parent_get_id(&output_parent, out);
EXPECT_EQ(strcmp(in, out), 0);
caosdb_entity_parent_get_name(&input_parent, in);
caosdb_entity_parent_get_name(&output_parent, out);
EXPECT_EQ(strcmp(in, out), 0);
// Delete everything
std::cout << "Deleting ..." << std::endl;
return_code = caosdb_entity_delete_parent(&input_parent);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_delete_property(&input_property);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_delete_entity(&entity);
EXPECT_EQ(return_code, 0);
// This tests the `_deletable` flag. The wrapped cpp objects of
// `output_parent` and `output_property` are owned by the entity, so
// they have been deleted together with the entity. With a wrong
// `_deletable` flag, the following would cause segfaults.
//
return_code = caosdb_entity_delete_parent(&output_parent);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_delete_property(&output_property);
EXPECT_EQ(return_code, 0);
}
TEST_F(test_ccaosdb, test_remove_property) {
caosdb_entity_entity entity;
int return_code(caosdb_entity_create_entity(&entity));
EXPECT_EQ(return_code, 0);
// Create two properties with names
caosdb_entity_property in_prop_1;
return_code = caosdb_entity_create_property(&in_prop_1);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_property_set_name(&in_prop_1, "Property 1");
EXPECT_EQ(return_code, 0);
caosdb_entity_property in_prop_2;
return_code = caosdb_entity_create_property(&in_prop_2);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_property_set_name(&in_prop_2, "Property 2");
EXPECT_EQ(return_code, 0);
// Append them
return_code = caosdb_entity_entity_append_property(&entity, &in_prop_1);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_entity_append_property(&entity, &in_prop_2);
EXPECT_EQ(return_code, 0);
// Delete one and see that the number of properties decreases by one
int count[] = {0}; // NOLINT
return_code = caosdb_entity_entity_get_properties_size(&entity, count);
EXPECT_EQ(return_code, 0);
EXPECT_EQ(*count, 2);
return_code = caosdb_entity_entity_remove_property(&entity, 0);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_entity_get_properties_size(&entity, count);
EXPECT_EQ(return_code, 0);
EXPECT_EQ(*count, 1);
caosdb_entity_property out_prop;
return_code = caosdb_entity_entity_get_property(&entity, &out_prop, 0);
EXPECT_EQ(return_code, 0);
char in[255] = {"a"}; // NOLINT
char out[255] = {"b"}; // NOLINT
// Deleted the first property, so the second one should remain.
return_code = caosdb_entity_property_get_name(&in_prop_2, in);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_property_get_name(&out_prop, out);
EXPECT_EQ(return_code, 0);
EXPECT_EQ(strcmp(in, out), 0);
// Delete everything we have created
return_code = caosdb_entity_delete_property(&in_prop_2);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_delete_property(&in_prop_1);
EXPECT_EQ(return_code, 0);
return_code = caosdb_entity_delete_entity(&entity);
EXPECT_EQ(return_code, 0);
}
......@@ -30,7 +30,9 @@
#include "gtest/gtest-message.h" // for Message
#include "gtest/gtest-test-part.h" // for TestPartResult, Sui...
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ
#include <memory> // for allocator, shared_ptr
#include <iostream>
#include <memory> // for allocator, shared_ptr
#include <string>
namespace caosdb::entity {
using caosdb::entity::v1alpha1::IdResponse;
......@@ -262,6 +264,111 @@ TEST(test_entity, test_from_id_response) {
EXPECT_EQ(other_ent.GetInfos().At(0).GetCode(), MessageCode::UNSPECIFIED);
}
TEST(test_entity, test_remove_property) {
Entity entity;
for (int i = 0; i < 5; i++) {
auto name = "PROPERTY-" + std::to_string(i);
Property property;
property.SetName(name);
EXPECT_EQ(property.GetName(), name);
entity.AppendProperty(property);
EXPECT_EQ(property.GetName(), name);
// not initializing the cache
}
ASSERT_EQ(entity.GetProperties().Size(), 5);
for (int i = 5; i < 10; i++) {
auto name = "PROPERTY-" + std::to_string(i);
Property property;
property.SetName(name);
EXPECT_EQ(property.GetName(), name);
entity.AppendProperty(property);
EXPECT_EQ(property.GetName(), name);
// initializing the cache
const auto &property_2 = entity.GetProperties().At(i);
EXPECT_EQ(property_2.GetName(), name);
}
ASSERT_EQ(entity.GetProperties().Size(), 10);
for (int i = 5; i < 10; i++) {
// double checking the cache
auto name = "PROPERTY-" + std::to_string(i);
const auto &property = entity.GetProperties().At(i);
EXPECT_EQ(property.GetName(), name);
}
// Remove at index 3
// P0,P1,P2,P3,P4,P5,P6,P7,P8,P9
// ^
// P0,P1,P2, P4,P5,P6,P7,P8,P9
entity.RemoveProperty(3);
// Remove at index 6
// P0,P1,P2, P4,P5,P6,P7,P8,P9
// ^
// P0,P1,P2, P4,P5,P6, P8,P9
entity.RemoveProperty(6);
ASSERT_EQ(entity.GetProperties().Size(), 8);
// AppendProperty another property
// P0,P1,P2, P4,P5,P6, P8,P9
// ^
// P0,P1,P2, P4,P5,P6, P8,P9,P10
Property property10;
property10.SetName("PROPERTY-10");
entity.AppendProperty(property10);
ASSERT_EQ(entity.GetProperties().Size(), 9);
std::cout << "[" << std::endl;
for (int i = 0; i < 9; i++) {
std::cout << " " << entity.GetProperties().At(i).GetName() << ",\n";
}
std::cout << "]" << std::endl;
for (int i = 0; i < 3; i++) {
auto name = "PROPERTY-" + std::to_string(i);
const auto &property = entity.GetProperties().At(i);
EXPECT_EQ(property.GetName(), name);
}
for (int i = 3; i < 6; i++) {
auto name = "PROPERTY-" + std::to_string(i + 1);
const auto &property = entity.GetProperties().At(i);
EXPECT_EQ(property.GetName(), name);
}
for (int i = 6; i < 9; i++) {
auto name = "PROPERTY-" + std::to_string(i + 2);
const auto &property = entity.GetProperties().At(i);
EXPECT_EQ(property.GetName(), name);
}
}
TEST(test_entity, test_property_iterator) {
Entity entity;
for (int i = 0; i < 5; i++) {
auto name = "PROPERTY-" + std::to_string(i);
Property property;
property.SetName(name);
EXPECT_EQ(property.GetName(), name);
entity.AppendProperty(property);
}
ASSERT_EQ(entity.GetProperties().Size(), 5);
int counter = 0;
for (auto &property : entity.GetProperties()) {
auto name = "PROPERTY-" + std::to_string(counter);
EXPECT_EQ(property.GetName(), name);
counter++;
}
EXPECT_EQ(counter, 5);
}
TEST(test_entity, test_description) {
Entity entity;
Property property;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment