Skip to content

Commit

Permalink
Merge pull request #2432 from kodebach/elektra_api
Browse files Browse the repository at this point in the history
highlevel: remove most of error handling from public API
  • Loading branch information
markus2330 committed Feb 25, 2019
2 parents f2b2d3c + 8115e3c commit 90ab22e
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 88 deletions.
2 changes: 1 addition & 1 deletion doc/news/_preparation_next_release.md
Expand Up @@ -66,7 +66,7 @@ types and functions have to be mapped to provide the full functionality.

Take a look at the [README](/src/libs/highlevel/README.md) for more infos.

For examples on how to build an application using this API take a look at our [example](/examples/highlevel). _(Klemens Böswirth)_
For an example on how to build an application using this API take a look at [this](/examples/highlevel). _(Klemens Böswirth)_

### <<HIGHLIGHT2>>

Expand Down
1 change: 1 addition & 0 deletions src/error/exporterrors.cpp
Expand Up @@ -524,6 +524,7 @@ static ostream & printPrivate (ostream & os, parse_t & p)
<< endl
<< "#include <elektra/types.h>" << endl
<< "#include <elektra/error.h>" << endl
<< "#include <kdbprivate.h>" << endl
<< endl
<< "#ifdef __cplusplus" << endl
<< "extern \"C\" {" << endl
Expand Down
25 changes: 0 additions & 25 deletions src/include/elektra/error.h
Expand Up @@ -20,34 +20,9 @@ extern "C" {

typedef struct _ElektraError ElektraError;

typedef enum
{
/**
* Use only, if the error will be raised with elektraFatalError().
*/
ELEKTRA_ERROR_SEVERITY_FATAL = 0,
ELEKTRA_ERROR_SEVERITY_ERROR,
ELEKTRA_ERROR_SEVERITY_WARNING
} ElektraErrorSeverity;

typedef const char * ElektraKDBErrorGroup;
typedef const char * ElektraKDBErrorModule;

typedef void (*ElektraErrorHandler) (ElektraError * error);

ElektraErrorCode elektraErrorCode (const ElektraError * error);
const char * elektraErrorDescription (const ElektraError * error);
ElektraErrorSeverity elektraErrorSeverity (const ElektraError * error);

int elektraKDBErrorCode (const ElektraError * error);
const char * elektraKDBErrorDescription (const ElektraError * error);
ElektraErrorSeverity elektraKDBErrorSeverity (const ElektraError * error);
ElektraKDBErrorGroup elektraKDBErrorGroup (const ElektraError * error);
ElektraKDBErrorModule elektraKDBErrorModule (const ElektraError * error);
const char * elektraKDBErrorReason (const ElektraError * error);
int elektraKDBErrorWarningCount (const ElektraError * error);
ElektraError * elektraKDBErrorGetWarning (const ElektraError * error, int index);
Key * elektraKDBErrorKey (const ElektraError * error);

void elektraErrorReset (ElektraError ** error);

Expand Down
27 changes: 27 additions & 0 deletions src/include/kdbprivate.h
Expand Up @@ -616,6 +616,19 @@ int elektraGlobalError (KDB * handle, KeySet * ks, Key * parentKey, int position
extern "C" {
#endif

typedef enum
{
/**
* Use only, if the error will be raised with elektraFatalError().
*/
ELEKTRA_ERROR_SEVERITY_FATAL = 0,
ELEKTRA_ERROR_SEVERITY_ERROR,
ELEKTRA_ERROR_SEVERITY_WARNING
} ElektraErrorSeverity;

typedef const char * ElektraKDBErrorGroup;
typedef const char * ElektraKDBErrorModule;

struct _Elektra
{
KDB * kdb;
Expand Down Expand Up @@ -652,6 +665,20 @@ void elektraSetLookupKey (Elektra * elektra, const char * name);
void elektraSetArrayLookupKey (Elektra * elektra, const char * name, kdb_long_long_t index);
ElektraError * elektraErrorCreate (ElektraErrorCode code, const char * description, ElektraErrorSeverity severity);

// error handling unstable/private for now
ElektraErrorCode elektraErrorCode (const ElektraError * error);
ElektraErrorSeverity elektraErrorSeverity (const ElektraError * error);

int elektraKDBErrorCode (const ElektraError * error);
const char * elektraKDBErrorDescription (const ElektraError * error);
ElektraErrorSeverity elektraKDBErrorSeverity (const ElektraError * error);
ElektraKDBErrorGroup elektraKDBErrorGroup (const ElektraError * error);
ElektraKDBErrorModule elektraKDBErrorModule (const ElektraError * error);
const char * elektraKDBErrorReason (const ElektraError * error);
int elektraKDBErrorWarningCount (const ElektraError * error);
ElektraError * elektraKDBErrorGetWarning (const ElektraError * error, int index);
Key * elektraKDBErrorKey (const ElektraError * error);

#ifdef __cplusplus
}
#undef Key
Expand Down
39 changes: 3 additions & 36 deletions src/libs/highlevel/README.md
Expand Up @@ -128,8 +128,9 @@ initializing a new variable with `ElektraError * error = NULL` or by reusing an
Notice, that you should always check if an error occurred by comparing it to `NULL` after the function call.

If an error happened, it is often useful to show an error message to the user. A description of what went wrong is provided in the
`ElektraError` struct and can be accessed using `elektraErrorDescription (error)`. A complete list of the provided accessors for
error-details can be found in [elektra_error.c](/src/libs/highlevel/elektra_error.c).
`ElektraError` struct and can be accessed using `elektraErrorDescription (error)`. Additionally the error code can be accessed through
`elektraErrorCode (error)`.
NOTE: The error API is still a work in progress, so more functions will likely be added in the future.

To avoid leakage of memory, you have to call `elektraErrorReset (&error)` (ideally as soon as you are finished resolving the error):

Expand All @@ -149,40 +150,6 @@ if (error != NULL)
}
```

#### Low-level Errors

Errors which do not originate inside the high-level API itself are wrapped into a `ElektraError` struct with error code
`ELEKTRA_ERROR_CODE_LOW_LEVEL`. The high-level Error API provides methods (`elektraKDBError*`) to access the properties of the low-level
error. You can also access the key to which the error was originally attached, as well as any possible low-level warnings.

To get the original low-level error code, description, severity, group, module and reason you can use these functions:

```c
int elektraKDBErrorCode (const ElektraError * error);
const char * elektraKDBErrorDescription (const ElektraError * error);
ElektraErrorSeverity elektraKDBErrorSeverity (const ElektraError * error);
ElektraKDBErrorGroup elektraKDBErrorGroup (const ElektraError * error);
ElektraKDBErrorModule elektraKDBErrorModule (const ElektraError * error);
const char * elektraKDBErrorReason (const ElektraError * error);
```
To iterate over all the warnings use the following to functions:
```c
int elektraKDBErrorWarningCount (const ElektraError * error);
ElektraError * elektraKDBErrorGetWarning (const ElektraError * error, int index);
```

`elektraKDBErrorGetWarning` will return a newly allocated `ElektraError` struct with error code `ELEKTRA_ERROR_CODE_LOW_LEVEL` and severity
`ELEKTRA_ERROR_SEVERITY_WARNING`. You will need to free the allocated struct when you are done. To access the information of the low-level
warning you use the `elektraKDBError*` functions described above.

The key to which the low-level error and the associated warnings where attached originally can be accessed via:

```c
Key * elektraKDBErrorKey (const ElektraError * error);
```
### Configuration

Currently there is only one way to configure an `Elektra` instance:
Expand Down
2 changes: 0 additions & 2 deletions src/libs/highlevel/elektra.c
Expand Up @@ -75,8 +75,6 @@ ELEKTRA_TAG_DEFINITIONS (kdb_long_double_t, LongDouble, KDB_TYPE_LONG_DOUBLE, el
*
* @return An Elektra instance initialized with the application.
*
* // TODO: examples
*
* @see elektraClose
*/
Elektra * elektraOpen (const char * application, KeySet * defaults, ElektraError ** error)
Expand Down
174 changes: 150 additions & 24 deletions tests/kdb/testkdb_highlevel.cpp
Expand Up @@ -26,6 +26,7 @@ static inline bool keyHasMetaValue (const kdb::Key & key, const std::string & me
return key && key.getMeta<std::string> (metaName) == value;
}

/* TODO: re-add once we have a stable public error API
constexpr const char * severityString (ElektraErrorSeverity severity)
{
return severity == ELEKTRA_ERROR_SEVERITY_ERROR ? "ERROR" : (severity == ELEKTRA_ERROR_SEVERITY_WARNING ? "WARNING" : "FATAL");
Expand Down Expand Up @@ -81,6 +82,7 @@ static std::ostream & operator<< (std::ostream & os, ElektraError ** error)
}
return os;
}
*/

class Highlevel : public ::testing::Test
{
Expand Down Expand Up @@ -121,8 +123,10 @@ class Highlevel : public ::testing::Test
static void fatalErrorHandler (ElektraError * error)
{
std::stringstream msg;
msg << "fatal error " << elektraErrorCode (error) << " in test "
<< ::testing::UnitTest::GetInstance ()->current_test_info ()->name () << ": " << &error << std::endl;
msg << "fatal error in test " << ::testing::UnitTest::GetInstance ()->current_test_info ()->name () << ": "
<< elektraErrorDescription (error) << std::endl;

elektraErrorReset (&error);

throw std::runtime_error (msg.str ());
}
Expand All @@ -139,7 +143,8 @@ class Highlevel : public ::testing::Test
elektraFatalErrorHandler (elektra, &fatalErrorHandler);
}

void setValues (std::initializer_list<kdb::Key> values)
template <class T>
void setValues (T values)
{
using namespace kdb;
KDB kdb;
Expand All @@ -153,6 +158,11 @@ class Highlevel : public ::testing::Test
kdb.set (config, testRoot);
}

void setValues (std::initializer_list<kdb::Key> values)
{
setValues<std::initializer_list<kdb::Key>> (values);
}

void setArrays (std::initializer_list<std::vector<kdb::Key>> arrays)
{
using namespace kdb;
Expand Down Expand Up @@ -193,6 +203,141 @@ class Highlevel : public ::testing::Test
const std::string Highlevel::configFile = "kdbFileHighlevel.dump";
const std::string Highlevel::testRoot = "/tests/highlevel/"; // DO NOT use namespace here, namespace would break testing::Mountpoint

TEST_F (Highlevel, CharTestGet)
{
std::vector<kdb::Key> keys;
for (int i = 0x01; i <= 0xFF; ++i)
{
auto c = static_cast<kdb_char_t> (i);
char s[] = { static_cast<char> (c), '\0' };
auto name = "char/_" + std::to_string (c);
keys.push_back (makeKey (KDB_TYPE_CHAR, name.c_str (), s));
}
setValues (keys);

createElektra ();

for (int i = 0x01; i <= 0xFF; ++i)
{
auto c = static_cast<kdb_char_t> (i);
auto name = "char/_" + std::to_string (c);
try
{
EXPECT_EQ (elektraGetChar (elektra, name.c_str ()), c) << "char " + name + " wrong";
}
catch (std::runtime_error &)
{
ADD_FAILURE () << "unexpected std::runtime_error thrown for " + name;
}
}
}

TEST_F (Highlevel, CharTestSet)
{
std::vector<kdb::Key> keys;
for (int i = 0x01; i <= 0xFF; ++i)
{
auto c = static_cast<kdb_char_t> (i);
auto name = "char/_" + std::to_string (c);
keys.push_back (makeKey (KDB_TYPE_CHAR, name.c_str (), c == '_' ? "0" : "_"));
}
setValues (keys);

createElektra ();

ElektraError * error = nullptr;

for (int i = 0x01; i <= 0xFF; ++i)
{
auto c = static_cast<kdb_char_t> (i);
auto name = "char/_" + std::to_string (c);
elektraSetChar (elektra, name.c_str (), c, &error);
if (error != nullptr)
{
ADD_FAILURE () << "error for char " + name;
}
}

using namespace kdb;
KDB kdb;
KeySet config;

kdb.get (config, testRoot);
for (int i = 0x01; i <= 0xFF; ++i)
{
auto c = static_cast<kdb_char_t> (i);
auto name = "char/_" + std::to_string (c);
char s[] = { static_cast<char> (c), '\0' };
EXPECT_KEYVALUE (config.lookup (testRoot + name), s) << "Wrong key value. for char " + name;
}
}

TEST_F (Highlevel, IntegerBordersGet)
{
setValues ({
makeKey (KDB_TYPE_OCTET, "octet/below", "-1"),
makeKey (KDB_TYPE_OCTET, "octet/min", "0"),
makeKey (KDB_TYPE_OCTET, "octet/max", "255"),
makeKey (KDB_TYPE_OCTET, "octet/above", "256"),
makeKey (KDB_TYPE_SHORT, "short/below", "-32769"),
makeKey (KDB_TYPE_SHORT, "short/min", "-32768"),
makeKey (KDB_TYPE_SHORT, "short/max", "32767"),
makeKey (KDB_TYPE_SHORT, "short/above", "32768"),
makeKey (KDB_TYPE_UNSIGNED_SHORT, "unsignedshort/below", "-1"),
makeKey (KDB_TYPE_UNSIGNED_SHORT, "unsignedshort/min", "0"),
makeKey (KDB_TYPE_UNSIGNED_SHORT, "unsignedshort/max", "65535"),
makeKey (KDB_TYPE_UNSIGNED_SHORT, "unsignedshort/above", "65536"),
makeKey (KDB_TYPE_LONG, "long/below", "-2147483649"),
makeKey (KDB_TYPE_LONG, "long/min", "-2147483648"),
makeKey (KDB_TYPE_LONG, "long/max", "2147483647"),
makeKey (KDB_TYPE_LONG, "long/above", "2147483648"),
makeKey (KDB_TYPE_UNSIGNED_LONG, "unsignedlong/below", "-1"),
makeKey (KDB_TYPE_UNSIGNED_LONG, "unsignedlong/min", "0"),
makeKey (KDB_TYPE_UNSIGNED_LONG, "unsignedlong/max", "4294967295"),
makeKey (KDB_TYPE_UNSIGNED_LONG, "unsignedlong/above", "4294967296"),
makeKey (KDB_TYPE_LONG_LONG, "longlong/below", "-9223372036854775809"),
makeKey (KDB_TYPE_LONG_LONG, "longlong/min", "-9223372036854775808"),
makeKey (KDB_TYPE_LONG_LONG, "longlong/max", "9223372036854775807"),
makeKey (KDB_TYPE_LONG_LONG, "longlong/above", "9223372036854775808"),
makeKey (KDB_TYPE_UNSIGNED_LONG_LONG, "unsignedlonglong/below", "-1"),
makeKey (KDB_TYPE_UNSIGNED_LONG_LONG, "unsignedlonglong/min", "0"),
makeKey (KDB_TYPE_UNSIGNED_LONG_LONG, "unsignedlonglong/max", "18446744073709551615"),
makeKey (KDB_TYPE_UNSIGNED_LONG_LONG, "unsignedlonglong/above", "18446744073709551616"),
});

createElektra ();

EXPECT_EQ (elektraGetOctet (elektra, "octet/min"), 0) << "octet/min wrong";
EXPECT_EQ (elektraGetOctet (elektra, "octet/max"), 0xff) << "octet/max wrong";
EXPECT_EQ (elektraGetShort (elektra, "short/min"), -0x8000) << "short/min wrong";
EXPECT_EQ (elektraGetShort (elektra, "short/max"), 0x7fFF) << "short/max wrong";
EXPECT_EQ (elektraGetUnsignedShort (elektra, "unsignedshort/min"), 0) << "unsignedshort/min wrong";
EXPECT_EQ (elektraGetUnsignedShort (elektra, "unsignedshort/max"), 0xffFF) << "unsignedshort/max wrong";
EXPECT_EQ (elektraGetLong (elektra, "long/min"), -0x80000000) << "long/min wrong";
EXPECT_EQ (elektraGetLong (elektra, "long/max"), 0x7fFFffFF) << "long/max wrong";
EXPECT_EQ (elektraGetUnsignedLong (elektra, "unsignedlong/min"), 0) << "unsignedlong/min wrong";
EXPECT_EQ (elektraGetUnsignedLong (elektra, "unsignedlong/max"), 0xffFFffFF) << "unsignedlong/max wrong";
EXPECT_EQ (elektraGetLongLong (elektra, "longlong/min"), -0x8000000000000000) << "longlong/min wrong";
EXPECT_EQ (elektraGetLongLong (elektra, "longlong/max"), 0x7fFFffFFffFFffFF) << "longlong/max wrong";
EXPECT_EQ (elektraGetUnsignedLongLong (elektra, "unsignedlonglong/min"), 0) << "unsignedlonglong/min wrong";
EXPECT_EQ (elektraGetUnsignedLongLong (elektra, "unsignedlonglong/max"), 0xffFFffFFffFFffFF) << "unsignedlonglong/max wrong";

EXPECT_THROW (elektraGetOctet (elektra, "octet/below"), std::runtime_error) << "octet/below accepted";
EXPECT_THROW (elektraGetOctet (elektra, "octet/above"), std::runtime_error) << "octet/above accepted";
EXPECT_THROW (elektraGetShort (elektra, "short/below"), std::runtime_error) << "short/below wrong";
EXPECT_THROW (elektraGetShort (elektra, "short/above"), std::runtime_error) << "short/above wrong";
EXPECT_THROW (elektraGetUnsignedShort (elektra, "unsignedshort/below"), std::runtime_error) << "unsignedshort/below wrong";
EXPECT_THROW (elektraGetUnsignedShort (elektra, "unsignedshort/above"), std::runtime_error) << "unsignedshort/above wrong";
EXPECT_THROW (elektraGetLong (elektra, "long/below"), std::runtime_error) << "long/below wrong";
EXPECT_THROW (elektraGetLong (elektra, "long/above"), std::runtime_error) << "long/above wrong";
EXPECT_THROW (elektraGetUnsignedLong (elektra, "unsignedlong/below"), std::runtime_error) << "unsignedlong/below wrong";
EXPECT_THROW (elektraGetUnsignedLong (elektra, "unsignedlong/above"), std::runtime_error) << "unsignedlong/above wrong";
EXPECT_THROW (elektraGetLongLong (elektra, "longlong/below"), std::runtime_error) << "longlong/below wrong";
EXPECT_THROW (elektraGetLongLong (elektra, "longlong/above"), std::runtime_error) << "longlong/above wrong";
EXPECT_THROW (elektraGetUnsignedLongLong (elektra, "unsignedlonglong/below"), std::runtime_error) << "unsignedlonglong/below wrong";
EXPECT_THROW (elektraGetUnsignedLongLong (elektra, "unsignedlonglong/above"), std::runtime_error) << "unsignedlonglong/above wrong";
}

TEST_F (Highlevel, PrimitveGetters)
{
setValues ({
Expand Down Expand Up @@ -865,27 +1010,8 @@ TEST_F (Highlevel, EnforceMetadata)

EXPECT_NE (elektraFindKey (elektra, "testkey", "long"), nullptr);

try
{
elektraFindKey (elektra, "testkey", "string");
ADD_FAILURE () << "expected std::runtime_error to be thrown";
}
catch (const std::runtime_error & err)
{
std::stringstream msg;
msg << "fatal error " << ELEKTRA_ERROR_CODE_WRONG_TYPE;

std::string errMsg = err.what ();

auto expected = msg.str ();
auto actual = errMsg.substr (0, expected.length ());

EXPECT_EQ (expected, actual);
}
catch (...)
{
ADD_FAILURE () << "expected std::runtime_error to be thrown";
}
EXPECT_THROW (elektraFindKey (elektra, "testkey", "string"), std::runtime_error);
// TODO: check error code once error API is public and stable

EXPECT_NE (elektraFindKey (elektra, "testkey", nullptr), nullptr);
}

0 comments on commit 90ab22e

Please sign in to comment.