diff --git a/doc/decisions/error_codes.md b/doc/decisions/error_codes.md index e5040eadc8d..64c4359d45a 100644 --- a/doc/decisions/error_codes.md +++ b/doc/decisions/error_codes.md @@ -58,7 +58,7 @@ Various projects and standards: Currently they have 43 classes which all come from SQLSTATE. Postgres also throws additional errors but have to subclass it to one of the current 43 classes and have a special naming convention which have to start with a `P` in the subclass. - [etcd](https://github.com/etcd-io/etcd): Etcd's approach for errors are tightly coupled to the programming language Go as well as the [gRPC](https://grpc.io/) standard which currently has - [16 codes](https://godoc.org/google.golang.org/grpc/codes) defined. Some of these errors are similar or identical to those which will be used in elektra. + [16 codes](https://pkg.go.dev/google.golang.org/grpc/codes?utm_source=godoc) defined. Some of these errors are similar or identical to those which will be used in elektra. Every error of etcd is associated with one of these categories and gets its own error message which is specified in [this](https://github.com/etcd-io/etcd/blob/master/etcdserver/api/v3rpc/rpctypes/error.go) file. This concept though does not allow easy subclassing which might be useful (eg. further split FailedPrecondition into more specific errors like semantic and syntactic errors) - [Windows Registry](https://docs.microsoft.com/en-us/windows/desktop/sysinfo/registry): The registry does not use any specific error concept but takes the standard [Win32 Error Codes](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d). These are neither hierarchical nor have any special ordering. Basically it is the same as elektra has now except for no duplicated diff --git a/doc/help/kdb-validate.md b/doc/help/kdb-validate.md new file mode 100644 index 00000000000..599aea5fd60 --- /dev/null +++ b/doc/help/kdb-validate.md @@ -0,0 +1,39 @@ +# kdb-validate(1) - Validate key values + +## SYNOPSIS + +`kdb validate` + +## DESCRIPTION + +Validate the values of string keys below a given name using the loaded validation plugins (eg. range or validation) by reading all values, making them dirty by changing to another value, changing back to original and then writing that back to the key database. + +This command is useful for validating configuration files against +their specifications. + +For keys to be validated, they must contain the 'check'-metakeys +and the respective plugins for validation must be loaded +for the backend that was used while mounting. +If a validation is done while using `kdb set` or `kdb get` +the same validation is also done by `kdb validate` +Only string keys are validated! Binary keys are skipped! + +Use `-f` to do a write-test even if the previous read +from the key database has issued warnings. + +## OPTIONS + +- `-d`,`--debug`: + Give debug information. +- `-f`, `--force`: + Force writing the configuration even on warnings. +- `-H`, `--help`: + Show the man page. +- `-p `, `--profile `: + Use a different profile for kdb configuration. +- `-v`, `--verbose`: + Explain what is happening. +- `-V`, `--version`: + Print version info. +- `-C `, `--color `: + Print `never/auto(default)/always` colored output. diff --git a/doc/man/man1/kdb-validate.1 b/doc/man/man1/kdb-validate.1 new file mode 100644 index 00000000000..29a6ff8294a --- /dev/null +++ b/doc/man/man1/kdb-validate.1 @@ -0,0 +1,38 @@ +.\" generated with Ronn-NG/v0.10.1 +.\" http://github.com/apjanke/ronn-ng/tree/0.10.1.pre1 +.TH "KDB\-VALIDATE" "1" "January 2022" "" +.SH "NAME" +\fBkdb\-validate\fR \- Validate key values +.SH "SYNOPSIS" +\fBkdb validate\fR +.SH "DESCRIPTION" +Validate the values of string keys below a given name using the loaded validation plugins (eg\. range or validation) by reading all values, making them dirty by changing to another value, changing back to original and then writing that back to the key database\. +.P +This command is useful for validating configuration files against their specifications\. +.P +For keys to be validated, they must contain the 'check'\-metakeys and the respective plugins for validation must be loaded for the backend that was used while mounting\. If a validation is done while using \fBkdb set\fR or \fBkdb get\fR the same validation is also done by \fBkdb validate\fR Only string keys are validated! Binary keys are skipped! +.P +Use \fB\-f\fR to do a write\-test even if the previous read from the key database has issued warnings\. +.SH "OPTIONS" +.TP +\fB\-d\fR,\fB\-\-debug\fR +Give debug information\. +.TP +\fB\-f\fR, \fB\-\-force\fR +Force writing the configuration even on warnings\. +.TP +\fB\-H\fR, \fB\-\-help\fR +Show the man page\. +.TP +\fB\-p \fR, \fB\-\-profile \fR +Use a different profile for kdb configuration\. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Explain what is happening\. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print version info\. +.TP +\fB\-C \fR, \fB\-\-color \fR +Print \fBnever/auto(default)/always\fR colored output\. + diff --git a/doc/man/man7/elektra-plugins.7 b/doc/man/man7/elektra-plugins.7 index 36caeb40d5b..5d9aa42463b 100644 --- a/doc/man/man7/elektra-plugins.7 +++ b/doc/man/man7/elektra-plugins.7 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.10.1 .\" http://github.com/apjanke/ronn-ng/tree/0.10.1.pre1 -.TH "ELEKTRA\-PLUGINS" "7" "July 2021" "" +.TH "ELEKTRA\-PLUGINS" "7" "January 2022" "" .SH "NAME" \fBelektra\-plugins\fR \- plugins overview .P @@ -94,7 +94,7 @@ augeas \fIaugeas/\fR reads/writes many different configuration files using the A .P Using semi\-structured data for config files, mainly suitable for spec\-namespace (put a focus on having nice syntax for metadata): .IP "\(bu" 4 -ni \fIni/\fR parses INI files based on (including metadata) ni \fIhttps://lab\.burn\.capital/chaz\-attic/bohr/\-/blob/main/include/bohr/ni\.h\fR\. +ni \fIni/\fR parses INI files based on (including metadata) .IP "" 0 .P Only suited for import/export: diff --git a/doc/news/_preparation_next_release.md b/doc/news/_preparation_next_release.md index 0b99f49407d..d9c6acd964f 100644 --- a/doc/news/_preparation_next_release.md +++ b/doc/news/_preparation_next_release.md @@ -150,7 +150,7 @@ _(Michael Tucek)_ ## Tools -- <> +- Implement `kdb validate `, collect warnings and errors while kdb.get() and kdb.set(), see #3674 _(@flo91)_, _(@JakobWonisch)_ - <> - <> diff --git a/doc/tutorials/highlevel-bindings.md b/doc/tutorials/highlevel-bindings.md index 06f9b0240c9..ceb12b91fac 100644 --- a/doc/tutorials/highlevel-bindings.md +++ b/doc/tutorials/highlevel-bindings.md @@ -56,7 +56,7 @@ direct mapping, significantly simplifies the generated code (and maybe also the ### Bindings for the `lowlevel` Library -Your programming language of choice must provide a way to call into C code (like [cgo](https://golang.org/cmd/cgo/)). +Your programming language of choice must provide a way to call into C code (like [cgo](https://pkg.go.dev/cmd/cgo/)). In general we prefer (in this order): diff --git a/doc/tutorials/install-webui.md b/doc/tutorials/install-webui.md index d3ea262ca52..0ba1516bbac 100644 --- a/doc/tutorials/install-webui.md +++ b/doc/tutorials/install-webui.md @@ -12,7 +12,7 @@ Elektra-web requires: - [Elektra](https://libelektra.org/) with the [`yajl` plugin](https://master.libelektra.org/src/plugins/yajl/) installed - A recent [node.js](https://nodejs.org/en/) installation (at least 6.x) -- [Go](https://golang.org/) with version > 1.13 +- [Go](https://go.dev/) with version > 1.13 ## Building with elektra-web Tool diff --git a/doc/tutorials/validation.md b/doc/tutorials/validation.md index fd4ec99c7db..63f6b31634b 100644 --- a/doc/tutorials/validation.md +++ b/doc/tutorials/validation.md @@ -301,3 +301,35 @@ rm $(kdb get system:/tests/userbackup) kdb rm system:/tests/specbackup kdb rm system:/tests/userbackup ``` + +## Validate Existing Keys + +To check if an existing set of keys can be read and written with the current validation rules `kdb validate` should be used. Validate will read the values of all string keys under the point defined as argument in the command line, sets the key value to something different, then back to the original and finally writes that original value back to the key database. All loaded [validation plugins](/src/plugins/README.md) are now used to validate the values of keys with the necessary meta-keys (see above). + +Only string keys are validated! Binary keys are skipped! + +```sh +# mount test config file and set a value +sudo kdb mount range.ecf /tests/range range dump + +# set value +kdb set user:/tests/range/value 5 + +# add range check to all keys under /tests/range/ +kdb meta-set spec:/tests/range/_ check/range "1-10" + +# check if validate passes +kdb validate /tests/range + +# set new key to invalid value (with kdb set -f) +kdb set -f user:/tests/range/value2 11 + +# validation fails now +kdb validate /tests/range +# RET:1 + +# clean up +kdb rm -r /tests/range/ +sudo kdb umount /tests/range + +``` diff --git a/src/libs/elektra/symbols.map b/src/libs/elektra/symbols.map index aa992f37570..53c0705667c 100644 --- a/src/libs/elektra/symbols.map +++ b/src/libs/elektra/symbols.map @@ -134,6 +134,27 @@ libelektra_1.0 { ELEKTRA_WARNING_VALIDATION_SEMANTIC; ELEKTRA_WARNING_VALIDATION_SYNTACTIC; + # export also names of errors and warnings + ELEKTRA_ERROR_CONFLICTING_STATE_NAME; + ELEKTRA_ERROR_INSTALLATION_NAME; + ELEKTRA_ERROR_INTERFACE_NAME; + ELEKTRA_ERROR_INTERNAL_NAME; + ELEKTRA_ERROR_OUT_OF_MEMORY_NAME; + ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR_NAME; + ELEKTRA_ERROR_RESOURCE_NAME; + ELEKTRA_ERROR_VALIDATION_SEMANTIC_NAME; + ELEKTRA_ERROR_VALIDATION_SYNTACTIC_NAME; + + ELEKTRA_WARNING_CONFLICTING_STATE_NAME; + ELEKTRA_WARNING_INSTALLATION_NAME; + ELEKTRA_WARNING_INTERFACE_NAME; + ELEKTRA_WARNING_INTERNAL_NAME; + ELEKTRA_WARNING_OUT_OF_MEMORY_NAME; + ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR_NAME; + ELEKTRA_WARNING_RESOURCE_NAME; + ELEKTRA_WARNING_VALIDATION_SEMANTIC_NAME; + ELEKTRA_WARNING_VALIDATION_SYNTACTIC_NAME; + # kdb.h elektraGOptsContract; elektraGOptsContractFromStrings; diff --git a/src/libs/tools/include/CMakeLists.txt b/src/libs/tools/include/CMakeLists.txt index fcb168b4330..292fb3bc91a 100644 --- a/src/libs/tools/include/CMakeLists.txt +++ b/src/libs/tools/include/CMakeLists.txt @@ -5,6 +5,13 @@ install ( DESTINATION include/${TARGET_INCLUDE_FOLDER} COMPONENT libelektra-dev) +file (GLOB ERROR_HDR_FILES errors/*.hpp) + +install ( + FILES ${ERROR_HDR_FILES} + DESTINATION include/${TARGET_INCLUDE_FOLDER}/errors + COMPONENT libelektra-dev) + file (GLOB HELPER_HDR_FILES helper/*.hpp) install ( diff --git a/src/libs/tools/include/errors/baseNotification.hpp b/src/libs/tools/include/errors/baseNotification.hpp new file mode 100644 index 00000000000..41adc81fe55 --- /dev/null +++ b/src/libs/tools/include/errors/baseNotification.hpp @@ -0,0 +1,107 @@ +#ifndef ELEKTRA_BASENOTIFICATION_HPP +#define ELEKTRA_BASENOTIFICATION_HPP + +#include +#include +#include +#include + +namespace kdb +{ +namespace tools +{ +namespace errors +{ +/* Common abstract class for warnings and errors. + * Because warning and errors share the same data members, + * a method can accept a BaseNotification argument and + * the caller can create an Error or a Warning + * based on the provided object. */ +class BaseNotification +{ +public: + /* constructor */ + BaseNotification (std::string reason, std::string module, std::string file, std::string mountPoint, std::string configFile, + kdb::long_t line); + + /* setters */ + void setData (const std::string & reason, const std::string & module, const std::string & file, const std::string & mountPoint, + const std::string & configFile, kdb::long_t line); + + /* get references (for setting and getting member values) */ + std::string & reason (); + std::string & module (); + std::string & file (); + std::string & mountPoint (); + std::string & configFile (); + kdb::long_t & line (); + const std::string & reason () const; + const std::string & module () const; + const std::string & file () const; + const std::string & mountPoint () const; + const std::string & configFile () const; + const kdb::long_t & line () const; + + /* fixed values per Class, taken from C-makro definitions in /src/include/kdberrors.h */ + virtual std::string code () const = 0; + virtual std::string description () const = 0; + + /* string representation */ + friend std::ostream & operator<< (std::ostream & outputStream, const BaseNotification & eb); + + /** + * @brief Compare fields of notification objects + * + * Also incorporates the compare method to enable subclasses to add constraints to the comparison. + * + * @param other the notification to compare + * + * @return true if objects are equal + */ + bool operator== (const BaseNotification & other) const; + bool operator!= (const BaseNotification & other) const; + +protected: + BaseNotification () = default; + + /** + * @brief Compare to another notification object + * + * Is used in operator==. + * Can be overloaded by subclasses to check additional constraints. + * At least the types of the two objects that get compared should be checked for equality! + * + * @param other the notification to compare to + * + * @return true if objects are equal + */ + virtual bool compare (const BaseNotification & other) const; + + /* Can be overwritten by subclasses to change the text representation */ + + /** + * @brief Get a text representation of the notification. + * + * Is used in operator<<. + * Can be overloaded by subclasses to append additional text. + * + * @param outputStream The stream to append the text to, + * used by the operator `<<` + * + * @return The given stream with additional text appended. + */ + virtual std::ostream & toString (std::ostream & outputStream) const; + +private: + std::string m_reason; + std::string m_module; + std::string m_file; + std::string m_mountPoint; + std::string m_configFile; + kdb::long_t m_line = 0; +}; + +} // namespace errors +} // namespace tools +} // namespace kdb +#endif // ELEKTRA_BASENOTIFICATION_HPP diff --git a/src/libs/tools/include/errors/error.hpp b/src/libs/tools/include/errors/error.hpp new file mode 100644 index 00000000000..c7c69fdf555 --- /dev/null +++ b/src/libs/tools/include/errors/error.hpp @@ -0,0 +1,105 @@ +#ifndef ELEKTRA_ERROR_HPP +#define ELEKTRA_ERROR_HPP + +#include "errors/warning.hpp" +#include + +namespace kdb +{ +namespace tools +{ + +namespace errors +{ + +class Error : public BaseNotification +{ +public: + /* inherit constructors */ + using BaseNotification::BaseNotification; + virtual ~Error (); + + /** + * @brief Add a warning to an error + * + * The warning is copied to make it independent from the source object. + * This way the same warning added to two different errors can be + * changed independently. + * + * An object of type `Error` can contain 0 to n warnings, + * like Keys in the C-API can contain one error, + * but multiple warnings. + * + * @param warning the warning to add + */ + void addWarning (Warning & warning); + + /* getters */ + kdb::long_t warningCount (); + + /* iterator functionality */ + std::vector::iterator begin () + { + return warnings.begin (); + } + std::vector::iterator end () + { + return warnings.end (); + } + std::vector::const_iterator begin () const + { + return warnings.begin (); + } + std::vector::const_iterator end () const + { + return warnings.begin (); + } + std::vector::const_iterator cbegin () const + { + return warnings.cbegin (); + } + std::vector::const_iterator cend () const + { + return warnings.cend (); + } + + /* get warning by index */ + Warning & operator[] (int index); + +private: + std::vector warnings; + +protected: + /** + * @brief Compare errors + * + * The comparison of data fields is done by operator== in the BaseNotification class. + * This function compares an errors warnings in addition to the notification fields. + * + * @param other the notification to compare to + * + * @return true if objects are equal + */ + bool compare (const BaseNotification & other) const override; + + /** + * @brief Create a text representation of the Error + * + * A string that contains the text representation + * defined by baseNotification.cpp as well as the + * text representation of all warnings that the + * Error object contains appended to the given stream. + * + * @param outputStream The stream were the string representations + * should be appended. + * + * @return The given stream with the created string + * written to it. + */ + std::ostream & toString (std::ostream & outputStream) const override; +}; +} // namespace errors +} // namespace tools +} // namespace kdb + +#endif // ELEKTRA_ERROR_HPP diff --git a/src/libs/tools/include/errors/errorFactory.hpp b/src/libs/tools/include/errors/errorFactory.hpp new file mode 100644 index 00000000000..33f291aeb4c --- /dev/null +++ b/src/libs/tools/include/errors/errorFactory.hpp @@ -0,0 +1,49 @@ +// +// Created by flo on 1/16/22. +// + +#ifndef ELEKTRA_ERRORFACTORY_HPP +#define ELEKTRA_ERRORFACTORY_HPP + +#include "error.hpp" + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +/* make sure to delete (free) the returned Errors */ +class ErrorFactory +{ +public: + ErrorFactory () = delete; + ~ErrorFactory () = delete; + + /* takes one of the ELEKTRA_ERROR_* constants (e.g. ELEKTRA_ERROR_OUT_OF_MEMORY) + * from /src/include/kdberrors.h as a parameter */ + static Error * create (const std::string & type, const std::string & reason, const std::string & module, const std::string & file, + const std::string & mountPoint, const std::string & configFile, kdb::long_t line); + + /** + * @brief Create an error from a given key + * + * Reads meta-keys of given key to find error and warnings meta-keys. If no error exists a PureWarningError is created that contains + * the key's warnings. + * + * @param key the key that has the error and warnings + * + * @return the error with warnings + */ + static Error * fromKey (kdb::Key key); + + /* checks if a code and description fit together */ + static bool checkErrorCodeDesc (const std::string & code, const std::string & description); +}; + +} // namespace errors +} // namespace tools +} // namespace kdb + +#endif // ELEKTRA_ERRORFACTORY_HPP diff --git a/src/libs/tools/include/errors/errorTypes.hpp b/src/libs/tools/include/errors/errorTypes.hpp new file mode 100644 index 00000000000..da3ba859d44 --- /dev/null +++ b/src/libs/tools/include/errors/errorTypes.hpp @@ -0,0 +1,137 @@ + +#ifndef ELEKTRA_ERRORTYPES_HPP +#define ELEKTRA_ERRORTYPES_HPP +#include "error.hpp" + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +/* Not an Error by itself, but a container for multiple Warnings, + * like keys in the C-API have at most one error, but 0 to n warnings. + * This way the content of such a key can be stored in a single Error object, + * even if the key doesn't contain an actual error. */ +class PureWarningError : public Error +{ +public: + PureWarningError () : Error{ "No error, only warnings.", "", "", "", "", 0 } + { + } + + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + + +/* Concrete classes for the different Errors, based on the constants defined in /src/include/kdberrors.h */ +class ResourceError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class OutOfMemoryError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class InstallationError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + + +class InternalError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class InterfaceError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class PluginMisbehaviorError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class ConflictingStateError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class ValidationSyntacticError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class ValidationSemanticError : public Error +{ +public: + using Error::Error; + std::string code () const override; + std::string description () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +} // namespace errors +} // namespace tools +} // namespace kdb + +#endif // ELEKTRA_ERRORTYPES_HPP diff --git a/src/libs/tools/include/errors/warning.hpp b/src/libs/tools/include/errors/warning.hpp new file mode 100644 index 00000000000..2e2ddf20f89 --- /dev/null +++ b/src/libs/tools/include/errors/warning.hpp @@ -0,0 +1,43 @@ + +#ifndef ELEKTRA_WARNING_HPP +#define ELEKTRA_WARNING_HPP + +#include "baseNotification.hpp" + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +/* The warning class currently has no extra parts compared to the BaseNotification class, + * it's used for distiguishing Warnings and Errors by their type and allow to only add Warnings to Errors. */ +class Warning : public BaseNotification +{ +public: + /* inherit constructors */ + using BaseNotification::BaseNotification; + virtual Warning * clone () const = 0; + + /* needed for freeing the elements of the Warning-container in Error-class */ + virtual ~Warning () = default; + +protected: + /** + * @brief Compare warnings + * + * The comparison of data fields is done by operator== in the BaseNotification class. + * This function compares the type of BaseNotification in addition to the notification fields. + * + * @param other the notification to compare to + * + * @return true if objects are equal + */ + bool compare (const BaseNotification & other) const override; +}; + +} // namespace errors +} // namespace tools +} // namespace kdb +#endif // ELEKTRA_WARNING_HPP diff --git a/src/libs/tools/include/errors/warningFactory.hpp b/src/libs/tools/include/errors/warningFactory.hpp new file mode 100644 index 00000000000..306a9d2009f --- /dev/null +++ b/src/libs/tools/include/errors/warningFactory.hpp @@ -0,0 +1,37 @@ +// +// Created by flo on 1/16/22. +// + +#ifndef ELEKTRA_WARNINGFACTORY_HPP +#define ELEKTRA_WARNINGFACTORY_HPP + +#include "warning.hpp" + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +class WarningFactory +{ +public: + WarningFactory () = delete; + ~WarningFactory () = delete; + + /* takes one of the ELEKTRA_WARNING_* constants (e.g. ELEKTRA_WARNING_OUT_OF_MEMORY) + * from /src/include/kdberrors.h as a parameter */ + /* You must delete the object that is returned by this function! */ + static Warning * create (const std::string & type, const std::string & reason, const std::string & module, const std::string & file, + const std::string & mountPoint, const std::string & configFile, kdb::long_t line); + + /* checks if a code and description fit together */ + static bool checkWarningCodeDesc (const std::string & code, const std::string & description); +}; + +} // namespace errors +} // namespace tools +} // namespace kdb + +#endif // ELEKTRA_WARNINGFACTORY_HPP diff --git a/src/libs/tools/include/errors/warningTypes.hpp b/src/libs/tools/include/errors/warningTypes.hpp new file mode 100644 index 00000000000..30033d0c743 --- /dev/null +++ b/src/libs/tools/include/errors/warningTypes.hpp @@ -0,0 +1,137 @@ + +#ifndef ELEKTRA_WARNINGTYPES_HPP +#define ELEKTRA_WARNINGTYPES_HPP + +#include "warning.hpp" + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +/* This file contains all concrete classes for the different Warnings, based on the constants defined in /src/include/kdberrors.h */ + +class ResourceWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + ResourceWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class OutOfMemoryWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + OutOfMemoryWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class InstallationWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + InstallationWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class InternalWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + InternalWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class InterfaceWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + InterfaceWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class PluginMisbehaviorWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + PluginMisbehaviorWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class ConflictingStateWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + ConflictingStateWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class ValidationSyntacticWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + ValidationSyntacticWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +class ValidationSemanticWarning : public Warning +{ +public: + using Warning::Warning; + + std::string code () const override; + std::string description () const override; + ValidationSemanticWarning * clone () const override; + +private: + bool compare (const BaseNotification & other) const final; +}; + +} // namespace errors +} // namespace tools +} // namespace kdb + +#endif // ELEKTRA_WARNINGTYPES_HPP diff --git a/src/libs/tools/src/errors/baseNotification.cpp b/src/libs/tools/src/errors/baseNotification.cpp new file mode 100644 index 00000000000..ff5e656c321 --- /dev/null +++ b/src/libs/tools/src/errors/baseNotification.cpp @@ -0,0 +1,122 @@ + +#include "errors/baseNotification.hpp" +#include +#include +#include + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +BaseNotification::BaseNotification (std::string reason, std::string module, std::string file, std::string mountPoint, + std::string configFile, kdb::long_t line) +: m_reason (std::move (reason)), m_module (std::move (module)), m_file (std::move (file)), m_mountPoint (std::move (mountPoint)), + m_configFile (std::move (configFile)), m_line (line) +{ +} + +void BaseNotification::setData (const std::string & reason, const std::string & module, const std::string & file, + const std::string & mountPoint, const std::string & configFile, kdb::long_t line) +{ + this->m_reason = reason; + this->m_module = module; + this->m_file = file; + this->m_mountPoint = mountPoint; + this->m_configFile = configFile; + this->m_line = line; +} + +/* String representation */ +std::ostream & BaseNotification::toString (std::ostream & outputStream) const +{ + return outputStream << "Code: " << code () << std::endl + << "Description: " << description () << std::endl + << "Reason: " << m_reason << std::endl + << "Module: " << m_module << std::endl + << "File: " << m_file << std::endl + << "Mount point: " << m_mountPoint << std::endl + << "Config file: " << m_configFile << std::endl + << "Line: " << std::to_string (m_line); +} + +std::ostream & operator<< (std::ostream & outputStream, const BaseNotification & eb) +{ + eb.toString (outputStream); + return outputStream; +} + +bool BaseNotification::compare (const BaseNotification & other ELEKTRA_UNUSED) const +{ + return true; +} + +bool BaseNotification::operator== (const BaseNotification & other) const +{ + return code () == other.code () && description () == other.description () && reason () == other.reason () && + module () == other.module () && file () == other.file () && mountPoint () == other.mountPoint () && + configFile () == other.configFile () && line () == other.line () && this->compare (other); +} + +bool BaseNotification::operator!= (const BaseNotification & other) const +{ + return !(*this == other); +} + +/* setters */ +std::string & BaseNotification::reason () +{ + return m_reason; +} +std::string & BaseNotification::module () +{ + return m_module; +} +std::string & BaseNotification::file () +{ + return m_file; +} +std::string & BaseNotification::mountPoint () +{ + return m_mountPoint; +} +std::string & BaseNotification::configFile () +{ + return m_configFile; +} +kdb::long_t & BaseNotification::line () +{ + return m_line; +} + +/* getters */ +const std::string & BaseNotification::reason () const +{ + return m_reason; +} +const std::string & BaseNotification::module () const +{ + return m_module; +} +const std::string & BaseNotification::file () const +{ + return m_file; +} +const std::string & BaseNotification::mountPoint () const +{ + return m_mountPoint; +} +const std::string & BaseNotification::configFile () const +{ + return m_configFile; +} +const kdb::long_t & BaseNotification::line () const +{ + return m_line; +} + +} // namespace errors +} // namespace tools +} // namespace kdb \ No newline at end of file diff --git a/src/libs/tools/src/errors/error.cpp b/src/libs/tools/src/errors/error.cpp new file mode 100644 index 00000000000..d6d1ca2f14b --- /dev/null +++ b/src/libs/tools/src/errors/error.cpp @@ -0,0 +1,101 @@ + +#include "errors/error.hpp" +#include "iostream" + + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + + +void Error::addWarning (Warning & warning) +{ + /* TODO: Decide if we should create copies or store the original warnings */ + warnings.push_back (warning.clone ()); +} + +/* getters */ +kdb::long_t Error::warningCount () +{ + return warnings.size (); +} + +Warning & Error::operator[] (int index) +{ + if (index >= warningCount ()) + { + throw std::out_of_range ("The warning with index " + std::to_string (index) + " was accessed, but there are only " + + std::to_string (warningCount ()) + " warnings stored in the Error-object!"); + } + else + { + return (*(warnings[index])); + } +} + +bool Error::compare (const BaseNotification & other) const +{ + /* comparison of data fields is done by operator== in BaseNotification class */ + const Error * pOtherError = dynamic_cast (&other); + if (!pOtherError || warnings.size () != pOtherError->warnings.size ()) + { + return false; + } + else + { + /* compare warnings */ + for (const Warning * w : warnings) + { + /* Two errors are equal if they contain the same warnings (compared by member values), + * even if they have different orders in the internal vector. */ + + /* TODO: Currently copies of warnings are stored by the addWarning(Warning&) method. + * If we decide to store the original warnings, then we have to decide if + * two different Warnings (not the same address in mem) are considered equal + * if the member values are equal. */ + bool equalWarningFound = false; + for (const Warning * ow : pOtherError->warnings) + { + if (*w == *ow) + { + equalWarningFound = true; + break; + } + } + + if (!equalWarningFound) + { + return false; + } + } + return true; + } +} +Error::~Error () +{ + for (Warning * w : warnings) + delete w; +} +std::ostream & Error::toString (std::ostream & outputStream) const +{ + BaseNotification::toString (outputStream); + + kdb::long_t i = 0; + if (warnings.size () > 0) + { + outputStream << std::endl << std::endl << "The following warnings were attachted to the Error: " << std::endl << std::endl; + for (const Warning * w : warnings) + outputStream << "Warning " << ++i << ": " << std::endl << *w << std::endl; + } + + + return outputStream; +} + + +} // namespace errors +} // namespace tools +} // namespace kdb \ No newline at end of file diff --git a/src/libs/tools/src/errors/errorFactory.cpp b/src/libs/tools/src/errors/errorFactory.cpp new file mode 100644 index 00000000000..698b6e04b80 --- /dev/null +++ b/src/libs/tools/src/errors/errorFactory.cpp @@ -0,0 +1,150 @@ + +#include "errors/errorFactory.hpp" +#include "errors/errorTypes.hpp" +#include "errors/warningFactory.hpp" +#include // for code and description constants +#include + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +Error * ErrorFactory::create (const std::string & type, const std::string & reason, const std::string & module, const std::string & file, + const std::string & mountPoint, const std::string & configFile, kdb::long_t line) +{ + if (type == ELEKTRA_ERROR_RESOURCE || type == ELEKTRA_ERROR_RESOURCE_NAME) + return new ResourceError (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_ERROR_OUT_OF_MEMORY || type == ELEKTRA_ERROR_OUT_OF_MEMORY_NAME) + return new OutOfMemoryError (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_ERROR_INSTALLATION || type == ELEKTRA_ERROR_INSTALLATION_NAME) + return new InstallationError (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_ERROR_INTERNAL || type == ELEKTRA_ERROR_INTERNAL_NAME) + return new InternalError (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_ERROR_INTERFACE || type == ELEKTRA_ERROR_INTERFACE_NAME) + return new InterfaceError (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR || type == ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR_NAME) + return new PluginMisbehaviorError (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_ERROR_CONFLICTING_STATE || type == ELEKTRA_ERROR_CONFLICTING_STATE_NAME) + return new ConflictingStateError (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_ERROR_VALIDATION_SYNTACTIC || type == ELEKTRA_ERROR_VALIDATION_SYNTACTIC_NAME) + return new ValidationSyntacticError (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_ERROR_VALIDATION_SEMANTIC || type == ELEKTRA_ERROR_VALIDATION_SEMANTIC_NAME) + return new ValidationSemanticError (reason, module, file, mountPoint, configFile, line); + else + return nullptr; +} + + +/* TODO: Test method */ +Error * ErrorFactory::fromKey (kdb::Key key) +{ + Error * err = nullptr; + if (key.isNull () || !key.isValid () || key.isBinary () || (!key.hasMeta ("error") && !key.hasMeta ("warnings"))) + { + return nullptr; + } + + if (!key.hasMeta ("error")) + { + err = new PureWarningError (); + } + else + { + std::string errCode = key.getMeta ("error/number"); + std::string errDesc = key.getMeta ("error/description"); + + if (!checkErrorCodeDesc (errCode, errDesc)) + { + /* TODO: throw exception */ + return nullptr; + } + else + { + std::string module = key.getMeta ("error/module"); + std::string file = key.getMeta ("error/file"); + std::string reason = key.getMeta ("error/reason"); + std::string mountPoint = key.getMeta ("error/mountpoint"); + std::string configFile = key.getMeta ("error/configfile"); + kdb::long_t line = key.getMeta ("error/line"); + + err = create (errCode, reason, module, file, mountPoint, configFile, line); + } + } + + /* process warnings * + * Code for extracting warnings was adapted from src/libs/highlevel/elektra_error.c:elektraErrorFromKey (Key * key) + * and /src/tools/kdb/coloredkdbio.h:printWarnings() + // TODO: use C++ binding version of keyMeta */ + KeySet metaKeys (ckdb::ksDup (ckdb::keyMeta (key.getKey ()))); + Key warningsParent ("meta:/warnings", KEY_END); + KeySet warningKeys = metaKeys.cut (warningsParent); + + if (warningKeys.size () > 0) + { + for (auto it = warningKeys.begin () + 1; it != warningKeys.end (); ++it) + { + if (it->isDirectBelow (warningsParent)) + { + auto name = it->getName (); + + std::string warnCode = warningKeys.get (name + "/number"); + std::string warnDescription = warningKeys.get (name + "/description"); + + if (!WarningFactory::checkWarningCodeDesc (warnCode, warnDescription)) + { + // TODO: throw exception + return nullptr; + } + + std::string warnReason = warningKeys.get (name + "/reason"); + std::string warnModule = warningKeys.get (name + "/module"); + std::string warnFile = warningKeys.get (name + "/file"); + + std::string warnMountPoint = warningKeys.get (name + "/mountpoint"); + std::string warnConfigFile = warningKeys.get (name + "/configfile"); + + kdb::long_t warnLine = warningKeys.get (name + "/line"); + + Warning * w = WarningFactory::create (warnCode, warnReason, warnModule, warnFile, warnMountPoint, + warnConfigFile, warnLine); + err->addWarning (*w); + + /* Warning gets copied by addWarning(Warning &) */ + delete w; + } + } + } + + return err; +} + +bool ErrorFactory::checkErrorCodeDesc (const std::string & code, const std::string & description) +{ + if (code == ELEKTRA_ERROR_RESOURCE) + return (description == ELEKTRA_ERROR_RESOURCE_NAME); + else if (code == ELEKTRA_ERROR_OUT_OF_MEMORY) + return (description == ELEKTRA_ERROR_OUT_OF_MEMORY_NAME); + else if (code == ELEKTRA_ERROR_INSTALLATION) + return (description == ELEKTRA_ERROR_INSTALLATION_NAME); + else if (code == ELEKTRA_ERROR_INTERNAL) + return (description == ELEKTRA_ERROR_INTERNAL_NAME); + else if (code == ELEKTRA_ERROR_INTERFACE) + return (description == ELEKTRA_ERROR_INTERFACE_NAME); + else if (code == ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR) + return (description == ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR_NAME); + else if (code == ELEKTRA_ERROR_CONFLICTING_STATE) + return (description == ELEKTRA_ERROR_CONFLICTING_STATE_NAME); + else if (code == ELEKTRA_ERROR_VALIDATION_SYNTACTIC) + return (description == ELEKTRA_ERROR_VALIDATION_SYNTACTIC_NAME); + else if (code == ELEKTRA_ERROR_VALIDATION_SEMANTIC) + return (description == ELEKTRA_ERROR_VALIDATION_SEMANTIC_NAME); + else + return false; +} + +} // namespace errors +} // namespace tools +} // namespace kdb \ No newline at end of file diff --git a/src/libs/tools/src/errors/errorTypes.cpp b/src/libs/tools/src/errors/errorTypes.cpp new file mode 100644 index 00000000000..f40d329d637 --- /dev/null +++ b/src/libs/tools/src/errors/errorTypes.cpp @@ -0,0 +1,175 @@ + +#include +#include // for code and description constants + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +std::string PureWarningError::code () const +{ + return ""; +} +std::string PureWarningError::description () const +{ + return "Warnings"; +} +bool PureWarningError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +std::string ResourceError::code () const +{ + return ELEKTRA_ERROR_RESOURCE; +} +std::string ResourceError::description () const +{ + return ELEKTRA_ERROR_RESOURCE_NAME; +} +bool ResourceError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + + +std::string OutOfMemoryError::code () const +{ + return ELEKTRA_ERROR_OUT_OF_MEMORY; +} +std::string OutOfMemoryError::description () const +{ + return ELEKTRA_ERROR_OUT_OF_MEMORY_NAME; +} +bool OutOfMemoryError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +std::string InstallationError::code () const +{ + return ELEKTRA_ERROR_INSTALLATION; +} +std::string InstallationError::description () const +{ + return ELEKTRA_ERROR_INSTALLATION_NAME; +} +bool InstallationError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +std::string InternalError::code () const +{ + return ELEKTRA_ERROR_INTERNAL; +} +std::string InternalError::description () const +{ + return ELEKTRA_ERROR_INTERNAL_NAME; +} +bool InternalError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +std::string InterfaceError::code () const +{ + return ELEKTRA_ERROR_INTERFACE; +} +std::string InterfaceError::description () const +{ + return ELEKTRA_ERROR_INTERFACE_NAME; +} +bool InterfaceError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +std::string PluginMisbehaviorError::code () const +{ + return ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR; +} +std::string PluginMisbehaviorError::description () const +{ + return ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR_NAME; +} +bool PluginMisbehaviorError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +std::string ConflictingStateError::code () const +{ + return ELEKTRA_ERROR_CONFLICTING_STATE; +} +std::string ConflictingStateError::description () const +{ + return ELEKTRA_ERROR_CONFLICTING_STATE_NAME; +} +bool ConflictingStateError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +std::string ValidationSyntacticError::code () const +{ + return ELEKTRA_ERROR_VALIDATION_SYNTACTIC; +} +std::string ValidationSyntacticError::description () const +{ + return ELEKTRA_ERROR_VALIDATION_SYNTACTIC_NAME; +} +bool ValidationSyntacticError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +std::string ValidationSemanticError::code () const +{ + return ELEKTRA_ERROR_VALIDATION_SEMANTIC; +} +std::string ValidationSemanticError::description () const +{ + return ELEKTRA_ERROR_VALIDATION_SEMANTIC_NAME; +} +bool ValidationSemanticError::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Error::compare (other); +} + +} // namespace errors +} // namespace tools +} // namespace kdb diff --git a/src/libs/tools/src/errors/warning.cpp b/src/libs/tools/src/errors/warning.cpp new file mode 100644 index 00000000000..679f39b7417 --- /dev/null +++ b/src/libs/tools/src/errors/warning.cpp @@ -0,0 +1,19 @@ + +#include "errors/warning.hpp" + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +bool Warning::compare (const BaseNotification & other) const +{ + /* comparison of data fields is done by operator== in BaseNotification class */ + return (dynamic_cast (&other)) ? true : false; +} + +} // namespace errors +} // namespace tools +} // namespace kdb \ No newline at end of file diff --git a/src/libs/tools/src/errors/warningFactory.cpp b/src/libs/tools/src/errors/warningFactory.cpp new file mode 100644 index 00000000000..0f355e77c6f --- /dev/null +++ b/src/libs/tools/src/errors/warningFactory.cpp @@ -0,0 +1,65 @@ + +#include "errors/warningFactory.hpp" +#include "errors/warningTypes.hpp" +#include // for code and description constants + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +Warning * WarningFactory::create (const std::string & type, const std::string & reason, const std::string & module, + const std::string & file, const std::string & mountPoint, const std::string & configFile, + kdb::long_t line) +{ + if (type == ELEKTRA_WARNING_RESOURCE || type == ELEKTRA_WARNING_RESOURCE_NAME) + return new ResourceWarning (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_WARNING_OUT_OF_MEMORY || type == ELEKTRA_WARNING_OUT_OF_MEMORY_NAME) + return new OutOfMemoryWarning (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_WARNING_INSTALLATION || type == ELEKTRA_WARNING_INSTALLATION_NAME) + return new InstallationWarning (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_WARNING_INTERNAL || type == ELEKTRA_WARNING_INTERNAL_NAME) + return new InternalWarning (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_WARNING_INTERFACE || type == ELEKTRA_WARNING_INTERFACE_NAME) + return new InterfaceWarning (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR || type == ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR_NAME) + return new PluginMisbehaviorWarning (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_WARNING_CONFLICTING_STATE || type == ELEKTRA_WARNING_CONFLICTING_STATE_NAME) + return new ConflictingStateWarning (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_WARNING_VALIDATION_SYNTACTIC || type == ELEKTRA_WARNING_VALIDATION_SYNTACTIC_NAME) + return new ValidationSyntacticWarning (reason, module, file, mountPoint, configFile, line); + else if (type == ELEKTRA_WARNING_VALIDATION_SEMANTIC || type == ELEKTRA_WARNING_VALIDATION_SEMANTIC_NAME) + return new ValidationSemanticWarning (reason, module, file, mountPoint, configFile, line); + else + return nullptr; +} + +bool WarningFactory::checkWarningCodeDesc (const std::string & code, const std::string & description) +{ + if (code == ELEKTRA_WARNING_RESOURCE) + return (description == ELEKTRA_WARNING_RESOURCE_NAME); + else if (code == ELEKTRA_WARNING_OUT_OF_MEMORY) + return (description == ELEKTRA_WARNING_OUT_OF_MEMORY_NAME); + else if (code == ELEKTRA_WARNING_INSTALLATION) + return (description == ELEKTRA_WARNING_INSTALLATION_NAME); + else if (code == ELEKTRA_WARNING_INTERNAL) + return (description == ELEKTRA_WARNING_INTERNAL_NAME); + else if (code == ELEKTRA_WARNING_INTERFACE) + return (description == ELEKTRA_WARNING_INTERFACE_NAME); + else if (code == ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR) + return (description == ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR_NAME); + else if (code == ELEKTRA_WARNING_CONFLICTING_STATE) + return (description == ELEKTRA_WARNING_CONFLICTING_STATE_NAME); + else if (code == ELEKTRA_WARNING_VALIDATION_SYNTACTIC) + return (description == ELEKTRA_WARNING_VALIDATION_SYNTACTIC_NAME); + else if (code == ELEKTRA_WARNING_VALIDATION_SEMANTIC) + return (description == ELEKTRA_WARNING_VALIDATION_SEMANTIC_NAME); + else + return false; +} + +} // namespace errors +} // namespace tools +} // namespace kdb \ No newline at end of file diff --git a/src/libs/tools/src/errors/warningTypes.cpp b/src/libs/tools/src/errors/warningTypes.cpp new file mode 100644 index 00000000000..ba2943ca007 --- /dev/null +++ b/src/libs/tools/src/errors/warningTypes.cpp @@ -0,0 +1,201 @@ + +#include +#include // for code and description constants + +namespace kdb +{ +namespace tools +{ +namespace errors +{ + +std::string ResourceWarning::code () const +{ + return ELEKTRA_WARNING_RESOURCE; +} +std::string ResourceWarning::description () const +{ + return ELEKTRA_WARNING_RESOURCE_NAME; +} +bool ResourceWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +ResourceWarning * ResourceWarning::clone () const +{ + return new ResourceWarning (*this); +} + +std::string OutOfMemoryWarning::code () const +{ + return ELEKTRA_WARNING_OUT_OF_MEMORY; +} +std::string OutOfMemoryWarning::description () const +{ + return ELEKTRA_WARNING_OUT_OF_MEMORY_NAME; +} +bool OutOfMemoryWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +OutOfMemoryWarning * OutOfMemoryWarning::clone () const +{ + return new OutOfMemoryWarning (*this); +} + + +std::string InstallationWarning::code () const +{ + return ELEKTRA_WARNING_INSTALLATION; +} +std::string InstallationWarning::description () const +{ + return ELEKTRA_WARNING_INSTALLATION_NAME; +} +bool InstallationWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +InstallationWarning * InstallationWarning::clone () const +{ + return new InstallationWarning (*this); +} + + +std::string InternalWarning::code () const +{ + return ELEKTRA_WARNING_INTERNAL; +} +std::string InternalWarning::description () const +{ + return ELEKTRA_WARNING_INTERNAL_NAME; +} +bool InternalWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +InternalWarning * InternalWarning::clone () const +{ + return new InternalWarning (*this); +} + + +std::string InterfaceWarning::code () const +{ + return ELEKTRA_WARNING_INTERFACE; +} +std::string InterfaceWarning::description () const +{ + return ELEKTRA_WARNING_INTERFACE_NAME; +} +bool InterfaceWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +InterfaceWarning * InterfaceWarning::clone () const +{ + return new InterfaceWarning (*this); +} + + +std::string PluginMisbehaviorWarning::code () const +{ + return ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR; +} +std::string PluginMisbehaviorWarning::description () const +{ + return ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR_NAME; +} +bool PluginMisbehaviorWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +PluginMisbehaviorWarning * PluginMisbehaviorWarning::clone () const +{ + return new PluginMisbehaviorWarning (*this); +} + +std::string ConflictingStateWarning::code () const +{ + return ELEKTRA_WARNING_CONFLICTING_STATE; +} +std::string ConflictingStateWarning::description () const +{ + return ELEKTRA_WARNING_CONFLICTING_STATE_NAME; +} +bool ConflictingStateWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +ConflictingStateWarning * ConflictingStateWarning::clone () const +{ + return new ConflictingStateWarning (*this); +} + + +std::string ValidationSyntacticWarning::code () const +{ + return ELEKTRA_WARNING_VALIDATION_SYNTACTIC; +} +std::string ValidationSyntacticWarning::description () const +{ + return ELEKTRA_WARNING_VALIDATION_SYNTACTIC_NAME; +} +bool ValidationSyntacticWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +ValidationSyntacticWarning * ValidationSyntacticWarning::clone () const +{ + return new ValidationSyntacticWarning (*this); +} + + +std::string ValidationSemanticWarning::code () const +{ + return ELEKTRA_WARNING_VALIDATION_SEMANTIC; +} +std::string ValidationSemanticWarning::description () const +{ + return ELEKTRA_WARNING_VALIDATION_SEMANTIC_NAME; +} +bool ValidationSemanticWarning::compare (const BaseNotification & other) const +{ + if (!(dynamic_cast (&other))) + return false; + else + return Warning::compare (other); +} +ValidationSemanticWarning * ValidationSemanticWarning::clone () const +{ + return new ValidationSemanticWarning (*this); +} + + +} // namespace errors +} // namespace tools +} // namespace kdb diff --git a/src/libs/tools/tests/testtool_error.cpp b/src/libs/tools/tests/testtool_error.cpp new file mode 100644 index 00000000000..6745c61fed8 --- /dev/null +++ b/src/libs/tools/tests/testtool_error.cpp @@ -0,0 +1,319 @@ +/** + * @file + * + * @brief Tests for the errors and warnings + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + * + */ + +#include +#include +#include +#include // for code and description constants + +TEST (Error, TestWarnings) +{ + /* Resource warning */ + std::cout << std::endl << "TEST WARNINGS:" << std::endl; + kdb::tools::errors::ResourceWarning resourceWarning ("resourceWarningReason", "resourceWarningModule", "resourceWarningFile", + "resourceWarningMountPoint", "resourceWarningConfigFile", 0); + ASSERT_EQ (resourceWarning.code (), ELEKTRA_WARNING_RESOURCE); + ASSERT_EQ (resourceWarning.description (), ELEKTRA_WARNING_RESOURCE_NAME); + ASSERT_EQ (resourceWarning.reason (), "resourceWarningReason"); + ASSERT_EQ (resourceWarning.module (), "resourceWarningModule"); + ASSERT_EQ (resourceWarning.file (), "resourceWarningFile"); + ASSERT_EQ (resourceWarning.mountPoint (), "resourceWarningMountPoint"); + ASSERT_EQ (resourceWarning.configFile (), "resourceWarningConfigFile"); + ASSERT_EQ (resourceWarning.line (), 0); + + /* Out of memory warning */ + kdb::tools::errors::OutOfMemoryWarning outOfMemoryWarning ("outOfMemoryWarningReason", "outOfMemoryWarningModule", + "outOfMemoryWarningFile", "outOfMemoryWarningMountPoint", + "outOfMemoryWarningConfigFile", 1); + ASSERT_EQ (outOfMemoryWarning.code (), ELEKTRA_WARNING_OUT_OF_MEMORY); + ASSERT_EQ (outOfMemoryWarning.description (), ELEKTRA_WARNING_OUT_OF_MEMORY_NAME); + ASSERT_EQ (outOfMemoryWarning.reason (), "outOfMemoryWarningReason"); + ASSERT_EQ (outOfMemoryWarning.module (), "outOfMemoryWarningModule"); + ASSERT_EQ (outOfMemoryWarning.file (), "outOfMemoryWarningFile"); + ASSERT_EQ (outOfMemoryWarning.mountPoint (), "outOfMemoryWarningMountPoint"); + ASSERT_EQ (outOfMemoryWarning.configFile (), "outOfMemoryWarningConfigFile"); + ASSERT_EQ (outOfMemoryWarning.line (), 1); + + /* Installation warning */ + kdb::tools::errors::InstallationWarning installationWarning ("installationWarningReason", "installationWarningModule", + "installationWarningFile", "installationWarningMountPoint", + "installationWarningConfigFile", -1); + ASSERT_EQ (installationWarning.code (), ELEKTRA_WARNING_INSTALLATION); + ASSERT_EQ (installationWarning.description (), ELEKTRA_WARNING_INSTALLATION_NAME); + ASSERT_EQ (installationWarning.reason (), "installationWarningReason"); + ASSERT_EQ (installationWarning.module (), "installationWarningModule"); + ASSERT_EQ (installationWarning.file (), "installationWarningFile"); + ASSERT_EQ (installationWarning.mountPoint (), "installationWarningMountPoint"); + ASSERT_EQ (installationWarning.configFile (), "installationWarningConfigFile"); + ASSERT_EQ (installationWarning.line (), -1); + + /* Internal warning */ + kdb::tools::errors::InternalWarning internalWarning ("internalWarningReason", "internalWarningModule", "internalWarningFile", + "internalWarningMountPoint", "internalWarningConfigFile", 999); + ASSERT_EQ (internalWarning.code (), ELEKTRA_WARNING_INTERNAL); + ASSERT_EQ (internalWarning.description (), ELEKTRA_WARNING_INTERNAL_NAME); + ASSERT_EQ (internalWarning.reason (), "internalWarningReason"); + ASSERT_EQ (internalWarning.module (), "internalWarningModule"); + ASSERT_EQ (internalWarning.file (), "internalWarningFile"); + ASSERT_EQ (internalWarning.mountPoint (), "internalWarningMountPoint"); + ASSERT_EQ (internalWarning.configFile (), "internalWarningConfigFile"); + ASSERT_EQ (internalWarning.line (), 999); + + /* Interface warning */ + kdb::tools::errors::InterfaceWarning interfaceWarning ("interfaceWarningReason", "interfaceWarningModule", "interfaceWarningFile", + "interfaceWarningMountPoint", "interfaceWarningConfigFile", 2); + ASSERT_EQ (interfaceWarning.code (), ELEKTRA_WARNING_INTERFACE); + ASSERT_EQ (interfaceWarning.description (), ELEKTRA_WARNING_INTERFACE_NAME); + ASSERT_EQ (interfaceWarning.reason (), "interfaceWarningReason"); + ASSERT_EQ (interfaceWarning.module (), "interfaceWarningModule"); + ASSERT_EQ (interfaceWarning.file (), "interfaceWarningFile"); + ASSERT_EQ (interfaceWarning.mountPoint (), "interfaceWarningMountPoint"); + ASSERT_EQ (interfaceWarning.configFile (), "interfaceWarningConfigFile"); + ASSERT_EQ (interfaceWarning.line (), 2); + + /* PluginMisbehavior warning */ + kdb::tools::errors::PluginMisbehaviorWarning pluginMisbehaviorWarning ( + "pluginMisbehaviorWarningReason", "pluginMisbehaviorWarningModule", "pluginMisbehaviorWarningFile", + "pluginMisbehaviorWarningMountPoint", "pluginMisbehaviorWarningConfigFile", 3); + ASSERT_EQ (pluginMisbehaviorWarning.code (), ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR); + ASSERT_EQ (pluginMisbehaviorWarning.description (), ELEKTRA_WARNING_PLUGIN_MISBEHAVIOR_NAME); + ASSERT_EQ (pluginMisbehaviorWarning.reason (), "pluginMisbehaviorWarningReason"); + ASSERT_EQ (pluginMisbehaviorWarning.module (), "pluginMisbehaviorWarningModule"); + ASSERT_EQ (pluginMisbehaviorWarning.file (), "pluginMisbehaviorWarningFile"); + ASSERT_EQ (pluginMisbehaviorWarning.mountPoint (), "pluginMisbehaviorWarningMountPoint"); + ASSERT_EQ (pluginMisbehaviorWarning.configFile (), "pluginMisbehaviorWarningConfigFile"); + ASSERT_EQ (pluginMisbehaviorWarning.line (), 3); + + /* ConflictingStatie warning */ + kdb::tools::errors::ConflictingStateWarning conflictingStateWarning ( + "conflictingStateWarningReason", "conflictingStateWarningModule", "conflictingStateWarningFile", + "conflictingStateWarningMountPoint", "conflictingStateWarningConfigFile", 4); + ASSERT_EQ (conflictingStateWarning.code (), ELEKTRA_WARNING_CONFLICTING_STATE); + ASSERT_EQ (conflictingStateWarning.description (), ELEKTRA_WARNING_CONFLICTING_STATE_NAME); + ASSERT_EQ (conflictingStateWarning.reason (), "conflictingStateWarningReason"); + ASSERT_EQ (conflictingStateWarning.module (), "conflictingStateWarningModule"); + ASSERT_EQ (conflictingStateWarning.file (), "conflictingStateWarningFile"); + ASSERT_EQ (conflictingStateWarning.mountPoint (), "conflictingStateWarningMountPoint"); + ASSERT_EQ (conflictingStateWarning.configFile (), "conflictingStateWarningConfigFile"); + ASSERT_EQ (conflictingStateWarning.line (), 4); + + /* ValidationSyntactic warning */ + kdb::tools::errors::ValidationSyntacticWarning validationSyntacticWarning ( + "validationSyntacticWarningReason", "validationSyntacticWarningModule", "validationSyntacticWarningFile", + "validationSyntacticMountPoint", "validationSyntacticWarningConfigFile", 5); + ASSERT_EQ (validationSyntacticWarning.code (), ELEKTRA_WARNING_VALIDATION_SYNTACTIC); + ASSERT_EQ (validationSyntacticWarning.description (), ELEKTRA_WARNING_VALIDATION_SYNTACTIC_NAME); + ASSERT_EQ (validationSyntacticWarning.reason (), "validationSyntacticWarningReason"); + ASSERT_EQ (validationSyntacticWarning.module (), "validationSyntacticWarningModule"); + ASSERT_EQ (validationSyntacticWarning.file (), "validationSyntacticWarningFile"); + ASSERT_EQ (validationSyntacticWarning.mountPoint (), "validationSyntacticMountPoint"); + ASSERT_EQ (validationSyntacticWarning.configFile (), "validationSyntacticWarningConfigFile"); + ASSERT_EQ (validationSyntacticWarning.line (), 5); + + /* ValidationSemantic warning */ + kdb::tools::errors::ValidationSemanticWarning validationSemanticWarning ( + "validationSemanticWarningReason", "validationSemanticWarningModule", "validationSemanticWarningFile", + "validationSemanticWarningMountPoint", "validationSemanticWarningConfigFile", 6); + ASSERT_EQ (validationSemanticWarning.code (), ELEKTRA_WARNING_VALIDATION_SEMANTIC); + ASSERT_EQ (validationSemanticWarning.description (), ELEKTRA_WARNING_VALIDATION_SEMANTIC_NAME); + ASSERT_EQ (validationSemanticWarning.reason (), "validationSemanticWarningReason"); + ASSERT_EQ (validationSemanticWarning.module (), "validationSemanticWarningModule"); + ASSERT_EQ (validationSemanticWarning.file (), "validationSemanticWarningFile"); + ASSERT_EQ (validationSemanticWarning.mountPoint (), "validationSemanticWarningMountPoint"); + ASSERT_EQ (validationSemanticWarning.configFile (), "validationSemanticWarningConfigFile"); + ASSERT_EQ (validationSemanticWarning.line (), 6); +} + +TEST (Error, TestErrors) +{ + /* Resource error */ + std::cout << std::endl << "TEST ERRORS:" << std::endl; + kdb::tools::errors::ResourceError resourceError ("resourceErrorReason", "resourceErrorModule", "resourceErrorFile", + "resourceErrorMountPoint", "resourceErrorConfigFile", 0); + ASSERT_EQ (resourceError.code (), ELEKTRA_ERROR_RESOURCE); + ASSERT_EQ (resourceError.description (), ELEKTRA_ERROR_RESOURCE_NAME); + ASSERT_EQ (resourceError.reason (), "resourceErrorReason"); + ASSERT_EQ (resourceError.module (), "resourceErrorModule"); + ASSERT_EQ (resourceError.file (), "resourceErrorFile"); + ASSERT_EQ (resourceError.mountPoint (), "resourceErrorMountPoint"); + ASSERT_EQ (resourceError.configFile (), "resourceErrorConfigFile"); + ASSERT_EQ (resourceError.line (), 0); + + /* Out of memory error */ + kdb::tools::errors::OutOfMemoryError outOfMemoryError ("outOfMemoryErrorReason", "outOfMemoryErrorModule", "outOfMemoryErrorFile", + "outOfMemoryErrorMountPoint", "outOfMemoryErrorConfigFile", 1); + ASSERT_EQ (outOfMemoryError.code (), ELEKTRA_ERROR_OUT_OF_MEMORY); + ASSERT_EQ (outOfMemoryError.description (), ELEKTRA_ERROR_OUT_OF_MEMORY_NAME); + ASSERT_EQ (outOfMemoryError.reason (), "outOfMemoryErrorReason"); + ASSERT_EQ (outOfMemoryError.module (), "outOfMemoryErrorModule"); + ASSERT_EQ (outOfMemoryError.file (), "outOfMemoryErrorFile"); + ASSERT_EQ (outOfMemoryError.mountPoint (), "outOfMemoryErrorMountPoint"); + ASSERT_EQ (outOfMemoryError.configFile (), "outOfMemoryErrorConfigFile"); + ASSERT_EQ (outOfMemoryError.line (), 1); + + /* Installation error */ + kdb::tools::errors::InstallationError installationError ("installationErrorReason", "installationErrorModule", + "installationErrorFile", "installationErrorMountPoint", + "installationErrorConfigFile", -1); + ASSERT_EQ (installationError.code (), ELEKTRA_ERROR_INSTALLATION); + ASSERT_EQ (installationError.description (), ELEKTRA_ERROR_INSTALLATION_NAME); + ASSERT_EQ (installationError.reason (), "installationErrorReason"); + ASSERT_EQ (installationError.module (), "installationErrorModule"); + ASSERT_EQ (installationError.file (), "installationErrorFile"); + ASSERT_EQ (installationError.mountPoint (), "installationErrorMountPoint"); + ASSERT_EQ (installationError.configFile (), "installationErrorConfigFile"); + ASSERT_EQ (installationError.line (), -1); + + /* Internal error */ + kdb::tools::errors::InternalError internalError ("internalErrorReason", "internalErrorModule", "internalErrorFile", + "internalErrorMountPoint", "internalErrorConfigFile", 999); + + ASSERT_EQ (internalError.code (), ELEKTRA_ERROR_INTERNAL); + ASSERT_EQ (internalError.description (), ELEKTRA_ERROR_INTERNAL_NAME); + ASSERT_EQ (internalError.reason (), "internalErrorReason"); + ASSERT_EQ (internalError.module (), "internalErrorModule"); + ASSERT_EQ (internalError.file (), "internalErrorFile"); + ASSERT_EQ (internalError.mountPoint (), "internalErrorMountPoint"); + ASSERT_EQ (internalError.configFile (), "internalErrorConfigFile"); + ASSERT_EQ (internalError.line (), 999); + + /* Interface error */ + kdb::tools::errors::InterfaceError interfaceError ("interfaceErrorReason", "interfaceErrorModule", "interfaceErrorFile", + "interfaceErrorMountPoint", "interfaceErrorConfigFile", 2); + ASSERT_EQ (interfaceError.code (), ELEKTRA_ERROR_INTERFACE); + ASSERT_EQ (interfaceError.description (), ELEKTRA_ERROR_INTERFACE_NAME); + ASSERT_EQ (interfaceError.reason (), "interfaceErrorReason"); + ASSERT_EQ (interfaceError.module (), "interfaceErrorModule"); + ASSERT_EQ (interfaceError.file (), "interfaceErrorFile"); + ASSERT_EQ (interfaceError.mountPoint (), "interfaceErrorMountPoint"); + ASSERT_EQ (interfaceError.configFile (), "interfaceErrorConfigFile"); + ASSERT_EQ (interfaceError.line (), 2); + + /* PluginMisbehavior error */ + kdb::tools::errors::PluginMisbehaviorError pluginMisbehaviorError ("pluginMisbehaviorErrorReason", "pluginMisbehaviorErrorModule", + "pluginMisbehaviorErrorFile", "pluginMisbehaviorErrorMountPoint", + "pluginMisbehaviorErrorConfigFile", 3); + ASSERT_EQ (pluginMisbehaviorError.code (), ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR); + ASSERT_EQ (pluginMisbehaviorError.description (), ELEKTRA_ERROR_PLUGIN_MISBEHAVIOR_NAME); + ASSERT_EQ (pluginMisbehaviorError.reason (), "pluginMisbehaviorErrorReason"); + ASSERT_EQ (pluginMisbehaviorError.module (), "pluginMisbehaviorErrorModule"); + ASSERT_EQ (pluginMisbehaviorError.file (), "pluginMisbehaviorErrorFile"); + ASSERT_EQ (pluginMisbehaviorError.mountPoint (), "pluginMisbehaviorErrorMountPoint"); + ASSERT_EQ (pluginMisbehaviorError.configFile (), "pluginMisbehaviorErrorConfigFile"); + ASSERT_EQ (pluginMisbehaviorError.line (), 3); + + /* ConflictingStatie error */ + kdb::tools::errors::ConflictingStateError conflictingStateError ("conflictingStateErrorReason", "conflictingStateErrorModule", + "conflictingStateErrorFile", "conflictingStateErrorMountPoint", + "conflictingStateErrorConfigFile", 4); + ASSERT_EQ (conflictingStateError.code (), ELEKTRA_ERROR_CONFLICTING_STATE); + ASSERT_EQ (conflictingStateError.description (), ELEKTRA_ERROR_CONFLICTING_STATE_NAME); + ASSERT_EQ (conflictingStateError.reason (), "conflictingStateErrorReason"); + ASSERT_EQ (conflictingStateError.module (), "conflictingStateErrorModule"); + ASSERT_EQ (conflictingStateError.file (), "conflictingStateErrorFile"); + ASSERT_EQ (conflictingStateError.mountPoint (), "conflictingStateErrorMountPoint"); + ASSERT_EQ (conflictingStateError.configFile (), "conflictingStateErrorConfigFile"); + ASSERT_EQ (conflictingStateError.line (), 4); + + /* ValidationSyntactic error */ + kdb::tools::errors::ValidationSyntacticError validationSyntacticError ( + "validationSyntacticErrorReason", "validationSyntacticErrorModule", "validationSyntacticErrorFile", + "validationSyntacticErrorMountPoint", "validationSyntacticErrorConfigFile", 5); + ASSERT_EQ (validationSyntacticError.code (), ELEKTRA_ERROR_VALIDATION_SYNTACTIC); + ASSERT_EQ (validationSyntacticError.description (), ELEKTRA_ERROR_VALIDATION_SYNTACTIC_NAME); + ASSERT_EQ (validationSyntacticError.reason (), "validationSyntacticErrorReason"); + ASSERT_EQ (validationSyntacticError.module (), "validationSyntacticErrorModule"); + ASSERT_EQ (validationSyntacticError.file (), "validationSyntacticErrorFile"); + ASSERT_EQ (validationSyntacticError.mountPoint (), "validationSyntacticErrorMountPoint"); + ASSERT_EQ (validationSyntacticError.configFile (), "validationSyntacticErrorConfigFile"); + ASSERT_EQ (validationSyntacticError.line (), 5); + + /* ValidationSemantic error */ + kdb::tools::errors::ValidationSemanticError validationSemanticError ( + "validationSemanticErrorReason", "validationSemanticErrorModule", "validationSemanticErrorFile", + "validationSemanticErrorMountPoint", "validationSemanticErrorConfigFile", 6); + ASSERT_EQ (validationSemanticError.code (), ELEKTRA_ERROR_VALIDATION_SEMANTIC); + ASSERT_EQ (validationSemanticError.description (), ELEKTRA_ERROR_VALIDATION_SEMANTIC_NAME); + ASSERT_EQ (validationSemanticError.reason (), "validationSemanticErrorReason"); + ASSERT_EQ (validationSemanticError.module (), "validationSemanticErrorModule"); + ASSERT_EQ (validationSemanticError.file (), "validationSemanticErrorFile"); + ASSERT_EQ (validationSemanticError.mountPoint (), "validationSemanticErrorMountPoint"); + ASSERT_EQ (validationSemanticError.configFile (), "validationSemanticErrorConfigFile"); + ASSERT_EQ (validationSemanticError.line (), 6); +} + +TEST (Error, ErrWarn) +{ + std::cout << std::endl << "TEST ERRORS WITH WARNINGS" << std::endl; + kdb::tools::errors::PluginMisbehaviorError pmE ("pmeReason", "pmeModule", "pmeFile", "pmeMountPoint", "pmeConfigFile", 100); + kdb::tools::errors::ConflictingStateWarning csW ("cswReason", "cswModule", "cswFile", "cswMountPoint", "cswConfigFile", 101); + kdb::tools::errors::InterfaceWarning ifW ("ifwReason", "ifwModule", "ifwFile", "ifwMountPoint", "ifwConfigFile", 102); + + pmE.addWarning (csW); + pmE.addWarning (ifW); + + ASSERT_EQ (pmE.warningCount (), 2); + + int i = 0; + for (kdb::tools::errors::Warning * w : pmE) + { + std::cout << std::endl << "WARNING IN ERROR: " << *w << std::endl; + + if (i++ == 0) + ASSERT_EQ (*w, csW); + else + ASSERT_EQ (*w, ifW); + } +} + +TEST (Error, Equality) +{ + std::cout << std::endl << "TEST EQUALITY OF WARNINGS AND ERRORS" << std::endl; + + kdb::tools::errors::InstallationWarning installationWarning ("Reason", "Module", "File", "MountPoint", "ConfigFile", 42); + kdb::tools::errors::InstallationWarning installationWarning2 ("Reason", "Module", "File", "MountPoint", "ConfigFile", 42); + kdb::tools::errors::InterfaceWarning interfaceWarning ("Reason", "Module", "File", "MountPoint", "ConfigFile", 42); + kdb::tools::errors::InstallationError installationError ("Reason", "Module", "File", "MountPoint", "ConfigFile", 42); + + ASSERT_EQ (installationWarning, installationWarning); + ASSERT_EQ (installationWarning, installationWarning2); + ASSERT_EQ (installationError, installationError); + ASSERT_NE (installationWarning, interfaceWarning); + ASSERT_NE (installationWarning, installationError); + ASSERT_NE (installationError, installationWarning); + + kdb::tools::errors::InstallationError installationError1 ("Reason", "Module", "File", "MountPoint", "ConfigFile", 42); + kdb::tools::errors::ResourceError resourceError ("Reason", "Module", "File", "MountPoint", "ConfigFile", 42); + + ASSERT_NE (installationError, resourceError); + ASSERT_EQ (installationError, installationError1); + + installationError.addWarning (installationWarning); + installationError.addWarning (installationWarning2); + installationError.addWarning (interfaceWarning); + ASSERT_NE (installationError, installationError1); + + installationError1.addWarning (interfaceWarning); + installationError1.addWarning (installationWarning); + ASSERT_NE (installationError, installationError1); + + installationError1.addWarning (installationWarning2); + /* Should be equal because same ordering of warnings is not necessary for equality */ + ASSERT_EQ (installationError, installationError1); + + resourceError.addWarning (installationWarning); + resourceError.addWarning (installationWarning2); + resourceError.addWarning (interfaceWarning); + ASSERT_NE (installationError, resourceError); + + /* currently the Warning gets copied when it is added to the Error */ + ASSERT_EQ (installationError[2].file (), installationError1[0].file ()); + installationError[2].file () = "changed file in warning"; + ASSERT_NE (installationError[2].file (), installationError1[0].file ()); +} \ No newline at end of file diff --git a/src/plugins/README.md b/src/plugins/README.md index 7fce748b7bb..5b37d3f3844 100644 --- a/src/plugins/README.md +++ b/src/plugins/README.md @@ -101,7 +101,6 @@ Using semi-structured data for config files, mainly suitable for spec-namespace (put a focus on having nice syntax for metadata): - [ni](ni/) parses INI files based on (including metadata) - [ni](https://lab.burn.capital/chaz-attic/bohr/-/blob/main/include/bohr/ni.h). Only suited for import/export: diff --git a/src/plugins/ni/README.md b/src/plugins/ni/README.md index 4f153a30f09..853be1018f1 100644 --- a/src/plugins/ni/README.md +++ b/src/plugins/ni/README.md @@ -127,5 +127,3 @@ The memory footprint is for a 190.000 (reduced to 35.000 when rewrote first ) line ini file with 1.1MB size is 16.88 MB. The sort order is not stable, even not with the same file rewritten again. - -[bohr libraries](https://lab.burn.capital/chaz-attic/bohr) diff --git a/src/plugins/range/range.c b/src/plugins/range/range.c index 2f8d015609e..e4940667517 100644 --- a/src/plugins/range/range.c +++ b/src/plugins/range/range.c @@ -520,6 +520,10 @@ int elektraRangeGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_U Key * cur; while ((cur = ksNext (returned)) != NULL) { + /* skip parent namespaces that differ from the key namespace, + * otherwise every warning would get issued multiple times */ + if (keyGetNamespace (parentKey) != keyGetNamespace (cur)) continue; + const Key * meta = keyGetMeta (cur, "check/range"); if (meta) { diff --git a/src/tools/kdb/cmdline.hpp b/src/tools/kdb/cmdline.hpp index 521e342a900..8916880da96 100644 --- a/src/tools/kdb/cmdline.hpp +++ b/src/tools/kdb/cmdline.hpp @@ -13,7 +13,7 @@ * * To add an option there are 5 steps. * Beware not to introduce options which already have - * an meaning in one of the utilities. + * a meaning in one of the utilities. * Please always append the options in alphabetical order * with capitals later. */ diff --git a/src/tools/kdb/coloredkdbio.hpp b/src/tools/kdb/coloredkdbio.hpp index f262ccdef0a..ade9b68d036 100644 --- a/src/tools/kdb/coloredkdbio.hpp +++ b/src/tools/kdb/coloredkdbio.hpp @@ -76,26 +76,43 @@ inline std::ostream & printWarnings (std::ostream & os, kdb::Key const & error, // TODO: use C++ binding version of keyMeta KeySet meta (ckdb::ksDup (ckdb::keyMeta (error.getKey ()))); Key parent ("meta:/warnings", KEY_END); - auto warnings = meta.cut (parent); - - int nr = warnings.size (); - if (nr == 0) + KeySet warnings = meta.cut (parent); + if (warnings.size () == 0) { return os; } - os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::MAGENTA) << " Sorry, " << nr + 1 << " warning" - << (!nr ? " was" : "s were") << " issued ;(" << getErrorColor (ANSI_COLOR::RESET) << std::endl; + // get number of warnings + Key keyMetaWarnings = warnings.lookup ("meta:/warnings"); + int cntWarnings = 0; + if (!keyMetaWarnings.isNull () && keyMetaWarnings.isValid ()) + { + std::string strWarningCount = keyMetaWarnings.getString (); + + // skip leading '#' and '_' characters + size_t i; + for (i = 0; i < strWarningCount.length () && (strWarningCount[i] == '#' || strWarningCount[i] == '_'); i++) + ; + strWarningCount = strWarningCount.substr (i); + cntWarnings = std::stoi (strWarningCount) + 1; + } + + os << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::MAGENTA) << " Sorry, " << cntWarnings << " warning" + << ((cntWarnings == 1) ? " was" : "s were") << " issued ;(" << getErrorColor (ANSI_COLOR::RESET) << std::endl; + cntWarnings = 0; for (auto it = warnings.begin () + 1; it != warnings.end (); ++it) { - auto name = it->getName (); + if (it->isDirectBelow (parent)) { - os << "\tSorry, module " << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::BLUE) - << warnings.get (name + "/module") << getErrorColor (ANSI_COLOR::RESET) - << " issued the warning " << getErrorColor (ANSI_COLOR::BOLD) << getErrorColor (ANSI_COLOR::RED) - << warnings.get (name + "/number") << getErrorColor (ANSI_COLOR::RESET) << ":" << std::endl; + auto name = it->getName (); + + os << ' ' << ++cntWarnings << ": Module " << getErrorColor (ANSI_COLOR::BOLD) + << getErrorColor (ANSI_COLOR::BLUE) << warnings.get (name + "/module") + << getErrorColor (ANSI_COLOR::RESET) << " issued the warning " << getErrorColor (ANSI_COLOR::BOLD) + << getErrorColor (ANSI_COLOR::RED) << warnings.get (name + "/number") + << getErrorColor (ANSI_COLOR::RESET) << ":" << std::endl; os << "\t" << warnings.get (name + "/description") << ": " << warnings.get (name + "/reason") << std::endl; if (printVerbose) diff --git a/src/tools/kdb/factory.hpp b/src/tools/kdb/factory.hpp index dae8cb2d0df..ab989706a4f 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -62,6 +62,7 @@ #include #include #include +#include class Instancer { @@ -130,6 +131,7 @@ class Factory m_factory.insert (std::make_pair ("namespace", std::make_shared> ())); m_factory.insert (std::make_pair ("basename", std::make_shared> ())); m_factory.insert (std::make_pair ("dirname", std::make_shared> ())); + m_factory.insert (std::make_pair ("validate", std::make_shared> ())); } std::vector getPrettyCommands () const diff --git a/src/tools/kdb/plugincheck.cpp b/src/tools/kdb/plugincheck.cpp index d251849b6fc..a4356e0d6db 100644 --- a/src/tools/kdb/plugincheck.cpp +++ b/src/tools/kdb/plugincheck.cpp @@ -34,7 +34,7 @@ int printProblems (Key const & k, std::string const & action, int off) return (wo + eo * 2) << off; } -int doKDBcheck (bool force) +int doKDBcheck () { Key x; try @@ -54,18 +54,7 @@ int doKDBcheck (bool force) } ret += printProblems (a, "getting", 2); - if (force) - { - Key b ("/", KEY_END); - try - { - kdb.set (ks, b); - } - catch (...) - { - } - ret += printProblems (b, "setting", 4); - } + /* write checks now handled by 'kdb validate */ Key y; kdb.close (y); @@ -83,7 +72,7 @@ int PluginCheckCommand::execute (Cmdline const & cl) { if (cl.arguments.size () == 0) { - return doKDBcheck (cl.force); + return doKDBcheck (); } std::string name = cl.arguments[0]; diff --git a/src/tools/kdb/plugincheck.hpp b/src/tools/kdb/plugincheck.hpp index 637a92bdc8a..0efca51b428 100644 --- a/src/tools/kdb/plugincheck.hpp +++ b/src/tools/kdb/plugincheck.hpp @@ -22,7 +22,7 @@ class PluginCheckCommand : public Command virtual std::string getShortOptions () override { - return "fc"; + return "c"; } virtual std::string getSynopsis () override @@ -37,11 +37,10 @@ class PluginCheckCommand : public Command virtual std::string getLongHelpText () override { - return "If no arguments are given checks on key database\n" - "are done instead. Use -f to also do a write test\n" - "(might change configuration files!)\n" + return "If no arguments are given, checks on the key database\n" + "are done instead.\n" "\n" - "If a plugin name is given, checks will only be done with given plugin.\n" + "If a plugin name is given, checks will only be done with the given plugin.\n" "Use -c to pass options to the plugin.\n" "\n" "Please report any output caused by official plugins to https://www.libelektra.org\n"; diff --git a/src/tools/kdb/validate.cpp b/src/tools/kdb/validate.cpp new file mode 100644 index 00000000000..7715b8c6bb8 --- /dev/null +++ b/src/tools/kdb/validate.cpp @@ -0,0 +1,150 @@ +/** + * @file + * + * @brief + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include +#include +#include /* for removeNamespace (Key) */ +#include + +using namespace std; +using namespace kdb; + + +/** + * @brief Validate key database subtree + * + * Validate the part of the database that is rooted by the key given in the first argument. + * This is done by reading all string key values, writing a different value, rewriting the original value and then re-setting the values + * in the key database. All loaded validation plugins are hereby triggered and their warnings are returned. + * Only string keys are validated! Binary keys will be skipped! + * + * @param cl the command line + * + * @return 0 if no warnings or errors were found and validation was therefore successful + * @return 1 if some warnings and errors occurred and validation therefore failed + */ +int ValidateCommand::execute (Cmdline const & cl) +{ + int argc = cl.arguments.size (); + if (argc != 1) + { + throw invalid_argument ("1 argument needed"); + } + + KeySet ksUnfiltered; + + /* use the given cmd line argument as the start key */ + Key root = cl.createKey (0); + + if (cl.verbose) + { + cout << "The name of the root key is: " + root.getName () << endl; + } + + /* Remove namespace -> create cascading key, so that + * check-constraints in the spec:/ namespace are considered. */ + root = removeNamespace (root); + + // do not resume on any get errors + // otherwise the user might break + // the config + kdb.get (ksUnfiltered, root); + + /* Convert result of kdb.get to Error object of the C++ errors/warnings API */ + tools::errors::Error * result = tools::errors::ErrorFactory::fromKey (root); + + /* If no warnings or errors occurred, the ErrorFactory returns a nullptr. */ + if (result) + { + cout << getFormattedInfoString ( + "The following warnings were issued while" + " trying to get the values of the keys: ") + << endl + << endl; + + cerr << *result << endl << endl; + + /* After printing the Warnings, the object is no longer needed. */ + delete result; + + if (!cl.force) + { + cerr << getFormattedErrorString ( + "The validation was stopped because of warnings " + "while getting the values!") + << endl; + return 1; + } + else if (cl.verbose) + { + cout << getFormattedInfoString ( + "Because -f was given, we now try to set the values " + "despite warnings during getting them...") + << endl; + } + } + else + { + cout << getFormattedSuccessString ("No warnings were issued! :)") << endl; + } + + KeySet ksPart (ksUnfiltered.cut (root)); + + /* iterate over the result keys */ + for (Key curKey : ksPart) + { + /* do lookup (needed for resolving cascading keys) */ + Key lookupKey = ksPart.lookup (curKey); + + /* only validate string keys */ + if (lookupKey.isBinary ()) continue; + + /* change value to enable sync flag */ + lookupKey.setString (lookupKey.getString () + "^"); + + /* change value back to original */ + std::string tmpStr = lookupKey.getString (); + + /* remove last char that was added in the previous step */ + tmpStr.pop_back (); + lookupKey.setString (tmpStr); + } + + /* write back values */ + try + { + kdb.set (ksPart, root); + return 0; + } + catch (KDBException & k) + { + cout << getFormattedInfoString ("The following error was issued while trying to set the values back: ") << endl << endl; + result = tools::errors::ErrorFactory::fromKey (root); + cerr << *result << endl << endl; + delete result; + return 1; + } +} + + +std::string ValidateCommand::getFormattedErrorString (const std::string & str) +{ + return getErrorColor (ANSI_COLOR::BOLD) + getErrorColor (ANSI_COLOR::MAGENTA) + str + getErrorColor (ANSI_COLOR::RESET); +} + +std::string ValidateCommand::getFormattedSuccessString (const std::string & str) +{ + return getStdColor (ANSI_COLOR::BOLD) + getStdColor (ANSI_COLOR::GREEN) + str + getStdColor (ANSI_COLOR::RESET); +} + +std::string ValidateCommand::getFormattedInfoString (const std::string & str) +{ + return getStdColor (ANSI_COLOR::BOLD) + getStdColor (ANSI_COLOR::YELLOW) + str + getStdColor (ANSI_COLOR::RESET); +} diff --git a/src/tools/kdb/validate.hpp b/src/tools/kdb/validate.hpp new file mode 100644 index 00000000000..3925cae0c22 --- /dev/null +++ b/src/tools/kdb/validate.hpp @@ -0,0 +1,62 @@ +/** + * @file + * + * @brief + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef VALIDATE_HPP +#define VALIDATE_HPP + +#include "coloredkdbio.hpp" +#include +#include + +class ValidateCommand : public Command +{ + kdb::KDB kdb; + +public: + ValidateCommand () = default; + ~ValidateCommand () override = default; + + virtual std::string getShortOptions () override + { + return "f"; + } + + virtual std::string getSynopsis () override + { + return ""; + } + + virtual std::string getShortHelpText () override + { + return "Validate the values of string keys below a given name."; + } + + virtual std::string getLongHelpText () override + { + return "This command is useful for validating configuration files against\n" + "their specifications.\n" + "For keys to be validated, they must contain the 'check'-metakeys\n" + "and the respective plugins for validation must be loaded\n" + "for the backend that was used while mounting.\n" + "If a validation is done while using 'kdb set'\n" + "the same validation is also done by 'kdb validate'\n" + "Only string keys are validated!\n" + "\n" + "Use -f to do a write test even if the previous read\n" + "from the key database has issued warnings.\n"; + } + + virtual int execute (Cmdline const & cmdline) override; + +private: + std::string getFormattedErrorString (const std::string &); + std::string getFormattedInfoString (const std::string &); + std::string getFormattedSuccessString (const std::string &); +}; + +#endif diff --git a/tests/linkchecker.whitelist b/tests/linkchecker.whitelist index 72a8a1c9c03..629e9c17b40 100644 --- a/tests/linkchecker.whitelist +++ b/tests/linkchecker.whitelist @@ -33,6 +33,7 @@ https://doc.libelektra.org/coverage/master/debian-buster-full https://crates.io/crates/elektra https://crates.io/crates/elektra-sys https://www.sciencedaily.com/releases/2005/11/051103080801.htm +https://lgtm.com # Certificate issues https://oyranos.org diff --git a/tests/shell/shell_recorder/tutorial_wrapper/CMakeLists.txt b/tests/shell/shell_recorder/tutorial_wrapper/CMakeLists.txt index 196880a8477..e912d49d96c 100644 --- a/tests/shell/shell_recorder/tutorial_wrapper/CMakeLists.txt +++ b/tests/shell/shell_recorder/tutorial_wrapper/CMakeLists.txt @@ -35,7 +35,7 @@ add_msr_test (cmerge "${CMAKE_SOURCE_DIR}/doc/tutorials/cmerge.md" REQUIRED_PLUG if (ENABLE_ASAN) message (STATUS "Excluding Markdown Shell Recorder test for `validation`, as it leaks memory and fails with ASAN enabled") else (ENABLE_ASAN) - add_msr_test (tutorial_validation "${CMAKE_SOURCE_DIR}/doc/tutorials/validation.md" REQUIRED_PLUGINS ni validation) + add_msr_test (tutorial_validation "${CMAKE_SOURCE_DIR}/doc/tutorials/validation.md" REQUIRED_PLUGINS ni validation range) add_msr_test ( tutorial_crypto "${CMAKE_SOURCE_DIR}/doc/tutorials/crypto.md" REQUIRED_PLUGINS crypto fcrypt