Merge lp:~marcustomlinson/unity-api/lp-1552082 into lp:unity-api

Proposed by Marcus Tomlinson
Status: Merged
Approved by: Michi Henning
Approved revision: 240
Merged at revision: 222
Proposed branch: lp:~marcustomlinson/unity-api/lp-1552082
Merge into: lp:unity-api
Diff against target: 958 lines (+604/-65)
7 files modified
CMakeLists.txt (+1/-1)
debian/changelog (+6/-0)
include/unity/util/IniParser.h (+62/-18)
src/unity/util/IniParser.cpp (+247/-16)
test/data/sample.ini (+2/-0)
test/gtest/unity/util/IniParser/CMakeLists.txt (+2/-0)
test/gtest/unity/util/IniParser/IniParser_test.cpp (+284/-30)
To merge this branch: bzr merge lp:~marcustomlinson/unity-api/lp-1552082
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Approve
Michi Henning (community) Approve
Paweł Stołowski (community) Needs Fixing
Review via email: mp+288895@code.launchpad.net

Commit message

Add set*() methods to IniParser

To post a comment you must log in.
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:219
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/34/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/922
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/938
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/938
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/936
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/936/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/936
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/936/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/936
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/936/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/936
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/936/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/936
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/936/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/936
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/936/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/34/rebuild

review: Approve (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Looks good! Only problem is missing file locking, mention in the review of https://code.launchpad.net/~marcustomlinson/unity-scopes-shell/lp-1552082/+merge/288918

Also, can you mention in the headers docs that sync() may throw? Can you add a test case for exception on sync()?

review: Needs Fixing
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> Looks good! Only problem is missing file locking, mention in the review of
> https://code.launchpad.net/~marcustomlinson/unity-scopes-
> shell/lp-1552082/+merge/288918
>
> Also, can you mention in the headers docs that sync() may throw? Can you add a
> test case for exception on sync()?

done.

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

@Pawel, Alright, this is ready for review again.

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Thanks Marcus. These changes look good when considered in isolation, but I'm afaird they have big impact in SettingsDB implementation in the scope. By having file locking implemented on the IniParser level, we're now locking twice (SettingsDB locks the file too); moreover there is some logic around in SettingsDB to give defaults if locking fails etc. If we want to go that route, then I suspect SettingsDB will need changes... Should we consider manual locking (by the called) instead? Let's discuss that in the standup.

review: Needs Fixing
Revision history for this message
Paweł Stołowski (stolowski) wrote :

> Thanks Marcus. These changes look good when considered in isolation, but I'm
> afaird they have big impact in SettingsDB implementation in the scope. By
> having file locking implemented on the IniParser level, we're now locking
> twice (SettingsDB locks the file too); moreover there is some logic around in
> SettingsDB to give defaults if locking fails etc. If we want to go that route,
> then I suspect SettingsDB will need changes... Should we consider manual
> locking (by the called) instead? Let's discuss that in the standup.

Uh, sorry, didn't mean to set it to 'needs fixing', but 'needs discussion' instead ;) As I said, nothing wrong with these changes in general, we just need to consider the implications.

review: Needs Information
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Oh, actually there is a problem.

+ fl.l_type = F_RDLCK;

We're using a read-lock for both reading and writing.

review: Needs Fixing
Revision history for this message
Michi Henning (michihenning) wrote :

The locking here is at the wrong level of abstraction, I believe. It is not necessary to lock at this level because there will be many cases where the writer and reader are the same single process.

The locking needs to be added at the level above, which knows whether a particular file might be accessed concurrently.

We already have locking in SettingsDB in scopes API. The locking needs to be moved in to the shell, to do the equivalent of what QSettings does.

review: Needs Fixing
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Indeed, for us to maintain behavioural compatibility, the file locking should occur on in unity-scopes-shell, as it was before via QSettings.

Revision history for this message
Michi Henning (michihenning) wrote :

One option to deal with this *might* be to make the SettingsDB class visible as part of the public API. That would allow the shell to just use it. Alternatively, steal the code from SettingsDB.cpp and put that into the shell. (It's not a lot of code.)

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> Thanks Marcus. These changes look good when considered in isolation, but I'm
> afaird they have big impact in SettingsDB implementation in the scope. By
> having file locking implemented on the IniParser level, we're now locking
> twice (SettingsDB locks the file too); moreover there is some logic around in
> SettingsDB to give defaults if locking fails etc. If we want to go that route,
> then I suspect SettingsDB will need changes... Should we consider manual
> locking (by the called) instead? Let's discuss that in the standup.

Fixed in https://code.launchpad.net/~marcustomlinson/unity-scopes-shell/lp-1552082/+merge/288918

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Ok, Michi made a good point this morning that the write functionality added to this class is looking too much like an after thought. As this lib is meant to serve a very wide audience, we need to ensure that the API is complete such that it is useful for wide range of applications (not just the shell plugin).

From a quick look over the API, one example of missing functionality is some way to delete groups and keys.

Let's hold back on landing this MP until I've managed to wrap up the API a little tighter.

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:230
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/50/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1139
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1122
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1122
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1120
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1120/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1120
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1120/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1120
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1120/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1120
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1120/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1120
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1120/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1120
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1120/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/50/rebuild

review: Approve (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:235
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/55/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1170
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1149
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1149
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1147
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1147/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1147
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1147/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1147
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1147/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1147
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1147/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1147
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1147/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1147
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1147/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/55/rebuild

review: Approve (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:238
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/59/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1177
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1154
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1154
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1152
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1152/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1152
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1152/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1152
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1152/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1152
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1152/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1152
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1152/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1152
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1152/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/59/rebuild

review: Approve (continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote :

Looks good! Nice coverage too! :-)

Some very minor code and doc quibbles below...

In the constructor and destructor, there is no need to lock the mutex.

"To extract / insert a value, simply specify the group and key
 names to the get* / set* methods of this class respectively."

The "/" needs to be close set. I'd also remove the "*", and a comma needs to be added:

"To extract/insert a value, simply specify the group and key
 names to the get/set methods of this class, respectively."

"The get methods indicate errors by throwing LogicExceptions."

I'd remove the plural:

"The get methods indicate errors by throwing LogicException."

"Examples why this might happen is because a value can't be
coerced into the given type (i.e trying to convert the value
"hello" into a boolean)."

"i.e" is missing a full stop here. But I'd be inclined to delete this sentence entirely because almost the same information is provided in the doc for the read methods below. For completeness, I'd reword that comment as:

    /** @name Read Methods
     * These member functions provide read access to configuration entries by group and key.<br>
     * Attempts to retrieve a value as the wrong type (such as retrieving a string value
     * as an integer) or to retrieve a value for a non-existent group or
     * key throw LogicException.
     **/

In the summary comment for the class, I'd add that all methods are thread-safe.

    /** @name Write Methods
     * These member functions provide write access to configuration entries by group and key.<br>
     * Attempts to remove groups or keys that do not exist, throw LogicException.
      **/

Remove the comma before "throw LogicException." I would also add:

       The set methods replace the value for a key if the key exists. Calling a set method for
       a non-existent group and/or key creates the group and/or key.

    /** @name Sync Method
     * This member function will write unsaved changes back to the configuration file.<br>
     * A failure to write to file will throw a FileException.
      **/

I would get rid of the future tense:

    /** @name Sync Method
     * This member function writes unsaved changes back to the configuration file.<br>
     * A failure to write to the file throws a FileException.
     **/

review: Needs Fixing
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> Looks good! Nice coverage too! :-)
>
> Some very minor code and doc quibbles below...
>
> In the constructor and destructor, there is no need to lock the mutex.

Sorry, I just had to have a look ;P

52.2.27 jussi.p | IniParser::IniParser(const char* filename)
52.2.12 jussi.p | {
120.1.1 michi.h | lock_guard<std::mutex> lock(internal::parser_mutex);

...

52.2.17 jussi.p | IniParser::~IniParser() noexcept
52.2.12 jussi.p | {
120.1.1 michi.h | lock_guard<std::mutex> lock(internal::parser_mutex);

>
> "To extract / insert a value, simply specify the group and key
> names to the get* / set* methods of this class respectively."
>
> The "/" needs to be close set. I'd also remove the "*", and a comma needs to
> be added:
>
> "To extract/insert a value, simply specify the group and key
> names to the get/set methods of this class, respectively."
>
> "The get methods indicate errors by throwing LogicExceptions."
>
> I'd remove the plural:
>
> "The get methods indicate errors by throwing LogicException."
>
> "Examples why this might happen is because a value can't be
> coerced into the given type (i.e trying to convert the value
> "hello" into a boolean)."
>
> "i.e" is missing a full stop here. But I'd be inclined to delete this sentence
> entirely because almost the same information is provided in the doc for the
> read methods below. For completeness, I'd reword that comment as:
>
> /** @name Read Methods
> * These member functions provide read access to configuration entries by
> group and key.<br>
> * Attempts to retrieve a value as the wrong type (such as retrieving a
> string value
> * as an integer) or to retrieve a value for a non-existent group or
> * key throw LogicException.
> **/
>
> In the summary comment for the class, I'd add that all methods are thread-
> safe.
>
> /** @name Write Methods
> * These member functions provide write access to configuration entries by
> group and key.<br>
> * Attempts to remove groups or keys that do not exist, throw
> LogicException.
> **/
>
> Remove the comma before "throw LogicException." I would also add:
>
> The set methods replace the value for a key if the key exists. Calling
> a set method for
> a non-existent group and/or key creates the group and/or key.
>
>
> /** @name Sync Method
> * This member function will write unsaved changes back to the
> configuration file.<br>
> * A failure to write to file will throw a FileException.
> **/
>
> I would get rid of the future tense:
>
> /** @name Sync Method
> * This member function writes unsaved changes back to the configuration
> file.<br>
> * A failure to write to the file throws a FileException.
> **/

Thanks Michi, done.

Revision history for this message
Michi Henning (michihenning) wrote :

> Sorry, I just had to have a look ;P

Yes, me bad. That was caused by a false positive from helgrind, from memory. The lock makes the FP go away, because helgrind doesn't understand atomics. I should have gone back and removed this again.

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:239
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/61/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1190
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1167
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1167
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1165
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1165/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1165
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1165/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1165
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1165/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1165
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1165/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1165
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1165/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1165
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1165/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/61/rebuild

review: Approve (continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote :

Looks good, thanks!

review: Approve
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:240
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/62/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1192
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1169
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1169
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1167
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1167/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1167
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/1167/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1167
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1167/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1167
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/1167/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1167
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1167/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1167
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/1167/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/62/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-08-11 14:05:12 +0000
+++ CMakeLists.txt 2016-03-30 08:50:57 +0000
@@ -122,7 +122,7 @@
122# API version122# API version
123set(UNITY_API_MAJOR 0)123set(UNITY_API_MAJOR 0)
124set(UNITY_API_MINOR 1)124set(UNITY_API_MINOR 1)
125set(UNITY_API_MICRO 5)125set(UNITY_API_MICRO 6)
126set(UNITY_API_VERSION "${UNITY_API_MAJOR}.${UNITY_API_MINOR}.${UNITY_API_MICRO}")126set(UNITY_API_VERSION "${UNITY_API_MAJOR}.${UNITY_API_MINOR}.${UNITY_API_MICRO}")
127127
128# API library128# API library
129129
=== modified file 'debian/changelog'
--- debian/changelog 2016-03-22 07:48:55 +0000
+++ debian/changelog 2016-03-30 08:50:57 +0000
@@ -1,3 +1,9 @@
1unity-api (7.109-0ubuntu1) UNRELEASED; urgency=medium
2
3 * Add set*() methods to IniParser
4
5 -- Marcus Tomlinson <marcus.tomlinson@canonical.com> Tue, 29 Mar 2016 08:32:15 +0200
6
1unity-api (7.108+16.04.20160322-0ubuntu1) xenial; urgency=medium7unity-api (7.108+16.04.20160322-0ubuntu1) xenial; urgency=medium
28
3 [ Lukáš Tinkl, Michał Sawicz ]9 [ Lukáš Tinkl, Michał Sawicz ]
410
=== modified file 'include/unity/util/IniParser.h'
--- include/unity/util/IniParser.h 2014-07-03 05:00:00 +0000
+++ include/unity/util/IniParser.h 2016-03-30 08:50:57 +0000
@@ -22,8 +22,8 @@
22#include <unity/SymbolExport.h>22#include <unity/SymbolExport.h>
23#include <unity/util/DefinesPtrs.h>23#include <unity/util/DefinesPtrs.h>
2424
25#include<string>25#include <string>
26#include<vector>26#include <vector>
2727
28namespace unity28namespace unity
29{29{
@@ -37,11 +37,11 @@
37}37}
3838
39/**39/**
40\brief Helper class to read configuration files.40\brief Helper class to read and write configuration files.
4141
42This class reads configuration files in the .ini format42This class reads configuration files in the .ini format
43and provides for a simple and type safe way of extracting43and provides for a simple and type safe way of extracting
44information. A typical ini file looks like this:44and inserting information. A typical ini file looks like this:
4545
46~~~46~~~
47[group1]47[group1]
@@ -55,14 +55,17 @@
55key2 = othervalue255key2 = othervalue2
56~~~56~~~
5757
58To obtain a value, simply specify the group and key names to58To extract/insert a value, simply specify the group and key
59the get* functions of this class. The array functions use59names to the get/set methods of this class, respectively.
60a semicolon as a separator.60The array methods use a semicolon as a separator.
6161
62The get functions indicate errors by throwing LogicExceptions.62To write unsaved changes back to the configuration file, call
63Examples why this might happen is because a value can't be63sync(). The sync() method will throw a FileException if it
64coerced into the given type (i.e trying to convert the value64fails to write to file.
65"hello" into a boolean).65
66The get methods indicate errors by throwing LogicException.
67
68All methods are thread-safe.
66*/69*/
6770
68class UNITY_API IniParser final {71class UNITY_API IniParser final {
@@ -78,31 +81,72 @@
78 IniParser() = delete;81 IniParser() = delete;
79 /// @endcond82 /// @endcond
8083
81 /** @name Accessors
82 * These member functions provide access to configuration entries by group and key.
83 * Attempts to retrieve a value as the wrong type, such as retrieving a string value
84 * "abc" as an integer, throw LogicException.
85 **/
86 //{@84 //{@
85
86 /** @name Read Methods
87 * These member functions provide read access to configuration entries by group and key.<br>
88 * Attempts to retrieve a value as the wrong type (such as retrieving a string value as an
89 * integer) or to retrieve a value for a non-existent group or key throw LogicException.
90 **/
91
87 bool has_group(const std::string& group) const noexcept;92 bool has_group(const std::string& group) const noexcept;
88 bool has_key(const std::string& group, const std::string& key) const;93 bool has_key(const std::string& group, const std::string& key) const;
94
89 std::string get_string(const std::string& group, const std::string& key) const;95 std::string get_string(const std::string& group, const std::string& key) const;
90 std::string get_locale_string(const std::string& group,96 std::string get_locale_string(const std::string& group,
91 const std::string& key,97 const std::string& key,
92 const std::string& locale = std::string()) const;98 const std::string& locale = std::string()) const;
93 bool get_boolean(const std::string& group, const std::string& key) const;99 bool get_boolean(const std::string& group, const std::string& key) const;
94 int get_int(const std::string& group, const std::string& key) const;100 int get_int(const std::string& group, const std::string& key) const;
101 double get_double(const std::string& group, const std::string& key) const;
95102
96 std::vector<std::string> get_string_array(const std::string& group, const std::string& key) const;103 std::vector<std::string> get_string_array(const std::string& group, const std::string& key) const;
97 std::vector<std::string> get_locale_string_array(const std::string& group,104 std::vector<std::string> get_locale_string_array(const std::string& group,
98 const std::string& key,105 const std::string& key,
99 const std::string& locale = std::string()) const;106 const std::string& locale = std::string()) const;
107 std::vector<bool> get_boolean_array(const std::string& group, const std::string& key) const;
100 std::vector<int> get_int_array(const std::string& group, const std::string& key) const;108 std::vector<int> get_int_array(const std::string& group, const std::string& key) const;
101 std::vector<bool> get_boolean_array(const std::string& group, const std::string& key) const;109 std::vector<double> get_double_array(const std::string& group, const std::string& key) const;
102110
103 std::string get_start_group() const;111 std::string get_start_group() const;
104 std::vector<std::string> get_groups() const;112 std::vector<std::string> get_groups() const;
105 std::vector<std::string> get_keys(const std::string& group) const;113 std::vector<std::string> get_keys(const std::string& group) const;
114
115 /** @name Write Methods
116 * These member functions provide write access to configuration entries by group and key.<br>
117 * Attempts to remove groups or keys that do not exist throw LogicException.<br>
118 * The set methods replace the value for a key if the key exists. Calling a set method for a
119 * non-existent group and/or key creates the group and/or key.
120 **/
121
122 bool remove_group(const std::string& group);
123 bool remove_key(const std::string& group, const std::string& key);
124
125 void set_string(const std::string& group, const std::string& key, const std::string& value);
126 void set_locale_string(const std::string& group,
127 const std::string& key,
128 const std::string& value,
129 const std::string& locale = std::string());
130 void set_boolean(const std::string& group, const std::string& key, bool value);
131 void set_int(const std::string& group, const std::string& key, int value);
132 void set_double(const std::string& group, const std::string& key, double value);
133
134 void set_string_array(const std::string& group, const std::string& key, const std::vector<std::string>& value);
135 void set_locale_string_array(const std::string& group,
136 const std::string& key,
137 const std::vector<std::string>& value,
138 const std::string& locale = std::string());
139 void set_boolean_array(const std::string& group, const std::string& key, const std::vector<bool>& value);
140 void set_int_array(const std::string& group, const std::string& key, const std::vector<int>& value);
141 void set_double_array(const std::string& group, const std::string& key, const std::vector<double>& value);
142
143 /** @name Sync Method
144 * This member function writes unsaved changes back to the configuration file.<br>
145 * A failure to write to the file throws a FileException.
146 **/
147
148 void sync();
149
106 //@}150 //@}
107151
108private:152private:
109153
=== modified file 'src/unity/util/IniParser.cpp'
--- src/unity/util/IniParser.cpp 2014-07-03 05:00:00 +0000
+++ src/unity/util/IniParser.cpp 2016-03-30 08:50:57 +0000
@@ -16,10 +16,12 @@
16 * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>16 * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>
17 */17 */
1818
19#include<unity/UnityExceptions.h>19#include <unity/UnityExceptions.h>
20#include<unity/util/IniParser.h>20#include <unity/util/IniParser.h>
21#include<glib.h>21
22#include<mutex>22#include <mutex>
23
24#include <glib.h>
2325
24using namespace std;26using namespace std;
2527
@@ -29,7 +31,6 @@
29namespace util31namespace util
30{32{
3133
32
33namespace internal34namespace internal
34{35{
3536
@@ -37,6 +38,7 @@
37{38{
38 GKeyFile *k;39 GKeyFile *k;
39 string filename;40 string filename;
41 bool dirty = false;
40};42};
4143
42static std::mutex parser_mutex;44static std::mutex parser_mutex;
@@ -68,8 +70,6 @@
6870
69IniParser::IniParser(const char* filename)71IniParser::IniParser(const char* filename)
70{72{
71 lock_guard<std::mutex> lock(internal::parser_mutex);
72
73 GKeyFile* kf = g_key_file_new();73 GKeyFile* kf = g_key_file_new();
74 GError* e = nullptr;74 GError* e = nullptr;
75 if (!kf)75 if (!kf)
@@ -95,14 +95,14 @@
9595
96IniParser::~IniParser() noexcept96IniParser::~IniParser() noexcept
97{97{
98 lock_guard<std::mutex> lock(internal::parser_mutex);
99
100 g_key_file_free(p->k);98 g_key_file_free(p->k);
101 delete p;99 delete p;
102}100}
103101
104bool IniParser::has_group(const std::string& group) const noexcept102bool IniParser::has_group(const std::string& group) const noexcept
105{103{
104 lock_guard<std::mutex> lock(internal::parser_mutex);
105
106 gboolean rval;106 gboolean rval;
107 rval = g_key_file_has_group(p->k, group.c_str());107 rval = g_key_file_has_group(p->k, group.c_str());
108 return rval;108 return rval;
@@ -110,6 +110,8 @@
110110
111bool IniParser::has_key(const std::string& group, const std::string& key) const111bool IniParser::has_key(const std::string& group, const std::string& key) const
112{112{
113 lock_guard<std::mutex> lock(internal::parser_mutex);
114
113 gboolean rval;115 gboolean rval;
114 GError* e = nullptr;116 GError* e = nullptr;
115 rval = g_key_file_has_key(p->k, group.c_str(), key.c_str(), &e);117 rval = g_key_file_has_key(p->k, group.c_str(), key.c_str(), &e);
@@ -119,6 +121,8 @@
119121
120std::string IniParser::get_string(const std::string& group, const std::string& key) const122std::string IniParser::get_string(const std::string& group, const std::string& key) const
121{123{
124 lock_guard<std::mutex> lock(internal::parser_mutex);
125
122 gchar* value;126 gchar* value;
123 GError* e = nullptr;127 GError* e = nullptr;
124 string result;128 string result;
@@ -131,6 +135,8 @@
131135
132std::string IniParser::get_locale_string(const std::string& group, const std::string& key, const std::string& locale) const136std::string IniParser::get_locale_string(const std::string& group, const std::string& key, const std::string& locale) const
133{137{
138 lock_guard<std::mutex> lock(internal::parser_mutex);
139
134 gchar* value;140 gchar* value;
135 GError* e = nullptr;141 GError* e = nullptr;
136 string result;142 string result;
@@ -143,6 +149,8 @@
143149
144bool IniParser::get_boolean(const std::string& group, const std::string& key) const150bool IniParser::get_boolean(const std::string& group, const std::string& key) const
145{151{
152 lock_guard<std::mutex> lock(internal::parser_mutex);
153
146 bool rval;154 bool rval;
147 GError* e = nullptr;155 GError* e = nullptr;
148 rval = g_key_file_get_boolean(p->k, group.c_str(), key.c_str(), &e);156 rval = g_key_file_get_boolean(p->k, group.c_str(), key.c_str(), &e);
@@ -152,6 +160,8 @@
152160
153int IniParser::get_int(const std::string& group, const std::string& key) const161int IniParser::get_int(const std::string& group, const std::string& key) const
154{162{
163 lock_guard<std::mutex> lock(internal::parser_mutex);
164
155 int rval;165 int rval;
156 GError* e = nullptr;166 GError* e = nullptr;
157 rval = g_key_file_get_integer(p->k, group.c_str(), key.c_str(), &e);167 rval = g_key_file_get_integer(p->k, group.c_str(), key.c_str(), &e);
@@ -159,8 +169,21 @@
159 return rval;169 return rval;
160}170}
161171
172double IniParser::get_double(const std::string& group, const std::string& key) const
173{
174 lock_guard<std::mutex> lock(internal::parser_mutex);
175
176 double rval;
177 GError* e = nullptr;
178 rval = g_key_file_get_double(p->k, group.c_str(), key.c_str(), &e);
179 inspect_error(e, "Could not get double value", p->filename, group);
180 return rval;
181}
182
162std::vector<std::string> IniParser::get_string_array(const std::string& group, const std::string& key) const183std::vector<std::string> IniParser::get_string_array(const std::string& group, const std::string& key) const
163{184{
185 lock_guard<std::mutex> lock(internal::parser_mutex);
186
164 vector<string> result;187 vector<string> result;
165 GError* e = nullptr;188 GError* e = nullptr;
166 gchar** strlist;189 gchar** strlist;
@@ -179,6 +202,8 @@
179 const std::string& key,202 const std::string& key,
180 const std::string& locale) const203 const std::string& locale) const
181{204{
205 lock_guard<std::mutex> lock(internal::parser_mutex);
206
182 vector<string> result;207 vector<string> result;
183 GError* e = nullptr;208 GError* e = nullptr;
184 gchar** strlist;209 gchar** strlist;
@@ -194,8 +219,28 @@
194 return result;219 return result;
195}220}
196221
222vector<bool> IniParser::get_boolean_array(const std::string& group, const std::string& key) const
223{
224 lock_guard<std::mutex> lock(internal::parser_mutex);
225
226 vector<bool> result;
227 GError* e = nullptr;
228 gboolean* bools;
229 gsize count;
230 bools = g_key_file_get_boolean_list(p->k, group.c_str(), key.c_str(), &count, &e);
231 inspect_error(e, "Could not get boolean array", p->filename, group);
232 for (gsize i = 0; i < count; i++)
233 {
234 result.push_back(bools[i]);
235 }
236 g_free(bools);
237 return result;
238}
239
197vector<int> IniParser::get_int_array(const std::string& group, const std::string& key) const240vector<int> IniParser::get_int_array(const std::string& group, const std::string& key) const
198{241{
242 lock_guard<std::mutex> lock(internal::parser_mutex);
243
199 vector<int> result;244 vector<int> result;
200 GError* e = nullptr;245 GError* e = nullptr;
201 gint* ints;246 gint* ints;
@@ -210,24 +255,28 @@
210 return result;255 return result;
211}256}
212257
213vector<bool> IniParser::get_boolean_array(const std::string& group, const std::string& key) const258vector<double> IniParser::get_double_array(const std::string& group, const std::string& key) const
214{259{
215 vector<bool> result;260 lock_guard<std::mutex> lock(internal::parser_mutex);
261
262 vector<double> result;
216 GError* e = nullptr;263 GError* e = nullptr;
217 gboolean* bools;264 gdouble* doubles;
218 gsize count;265 gsize count;
219 bools = g_key_file_get_boolean_list(p->k, group.c_str(), key.c_str(), &count, &e);266 doubles = g_key_file_get_double_list(p->k, group.c_str(), key.c_str(), &count, &e);
220 inspect_error(e, "Could not get boolean array", p->filename, group);267 inspect_error(e, "Could not get double array", p->filename, group);
221 for (gsize i = 0; i < count; i++)268 for (gsize i = 0; i < count; i++)
222 {269 {
223 result.push_back(bools[i]);270 result.push_back(doubles[i]);
224 }271 }
225 g_free(bools);272 g_free(doubles);
226 return result;273 return result;
227}274}
228275
229string IniParser::get_start_group() const276string IniParser::get_start_group() const
230{277{
278 lock_guard<std::mutex> lock(internal::parser_mutex);
279
231 gchar* sg = g_key_file_get_start_group(p->k);280 gchar* sg = g_key_file_get_start_group(p->k);
232 string result(sg);281 string result(sg);
233 g_free(sg);282 g_free(sg);
@@ -236,6 +285,8 @@
236285
237vector<string> IniParser::get_groups() const286vector<string> IniParser::get_groups() const
238{287{
288 lock_guard<std::mutex> lock(internal::parser_mutex);
289
239 vector<string> result;290 vector<string> result;
240 gsize count;291 gsize count;
241 gchar** groups = g_key_file_get_groups(p->k, &count);292 gchar** groups = g_key_file_get_groups(p->k, &count);
@@ -249,6 +300,8 @@
249300
250vector<string> IniParser::get_keys(const std::string& group) const301vector<string> IniParser::get_keys(const std::string& group) const
251{302{
303 lock_guard<std::mutex> lock(internal::parser_mutex);
304
252 vector<string> result;305 vector<string> result;
253 GError* e = nullptr;306 GError* e = nullptr;
254 gchar** strlist;307 gchar** strlist;
@@ -264,6 +317,184 @@
264 return result;317 return result;
265}318}
266319
320bool IniParser::remove_group(const std::string& group)
321{
322 lock_guard<std::mutex> lock(internal::parser_mutex);
323
324 gboolean rval;
325 GError* e = nullptr;
326 rval = g_key_file_remove_group(p->k, group.c_str(), &e);
327 inspect_error(e, "Error removing group", p->filename, group);
328 return rval;
329}
330
331bool IniParser::remove_key(const std::string& group, const std::string& key)
332{
333 lock_guard<std::mutex> lock(internal::parser_mutex);
334
335 gboolean rval;
336 GError* e = nullptr;
337 rval = g_key_file_remove_key(p->k, group.c_str(), key.c_str(), &e);
338 inspect_error(e, "Error removing key", p->filename, group);
339 return rval;
340}
341
342void IniParser::set_string(const std::string& group, const std::string& key, const std::string& value)
343{
344 lock_guard<std::mutex> lock(internal::parser_mutex);
345
346 g_key_file_set_string(p->k, group.c_str(), key.c_str(), value.c_str());
347 p->dirty = true;
348}
349
350void IniParser::set_locale_string(const std::string& group, const std::string& key,
351 const std::string& value, const std::string& locale)
352{
353 lock_guard<std::mutex> lock(internal::parser_mutex);
354
355 g_key_file_set_locale_string(p->k, group.c_str(), key.c_str(), locale.c_str(), value.c_str());
356 p->dirty = true;
357}
358
359void IniParser::set_boolean(const std::string& group, const std::string& key, bool value)
360{
361 lock_guard<std::mutex> lock(internal::parser_mutex);
362
363 g_key_file_set_boolean(p->k, group.c_str(), key.c_str(), value);
364 p->dirty = true;
365}
366
367void IniParser::set_int(const std::string& group, const std::string& key, int value)
368{
369 lock_guard<std::mutex> lock(internal::parser_mutex);
370
371 g_key_file_set_integer(p->k, group.c_str(), key.c_str(), value);
372 p->dirty = true;
373}
374
375void IniParser::set_double(const std::string& group, const std::string& key, double value)
376{
377 lock_guard<std::mutex> lock(internal::parser_mutex);
378
379 g_key_file_set_double(p->k, group.c_str(), key.c_str(), value);
380 p->dirty = true;
381}
382
383void IniParser::set_string_array(const std::string& group, const std::string& key,
384 const std::vector<std::string>& value)
385{
386 lock_guard<std::mutex> lock(internal::parser_mutex);
387
388 int count = value.size();
389 gchar** strlist = g_new(gchar*, count+1);
390
391 for (int i = 0; i < count; ++i)
392 {
393 strlist[i] = g_strdup(value[i].c_str());
394 }
395 strlist[count] = nullptr;
396
397 g_key_file_set_string_list(p->k, group.c_str(), key.c_str(), strlist, count);
398 p->dirty = true;
399
400 g_strfreev(strlist);
401}
402
403void IniParser::set_locale_string_array(const std::string& group, const std::string& key,
404 const std::vector<std::string>& value, const std::string& locale)
405{
406 lock_guard<std::mutex> lock(internal::parser_mutex);
407
408 int count = value.size();
409 gchar** strlist = g_new(gchar*, count+1);
410
411 for (int i = 0; i < count; ++i)
412 {
413 strlist[i] = g_strdup(value[i].c_str());
414 }
415 strlist[count] = nullptr;
416
417 g_key_file_set_locale_string_list(p->k, group.c_str(), key.c_str(), locale.c_str(), strlist, count);
418 p->dirty = true;
419
420 g_strfreev(strlist);
421}
422
423void IniParser::set_boolean_array(const std::string& group, const std::string& key, const std::vector<bool>& value)
424{
425 lock_guard<std::mutex> lock(internal::parser_mutex);
426
427 int count = value.size();
428 gboolean* boollist = g_new(gboolean, count);
429
430 for (int i = 0; i < count; ++i)
431 {
432 boollist[i] = value[i];
433 }
434
435 g_key_file_set_boolean_list(p->k, group.c_str(), key.c_str(), boollist, count);
436 p->dirty = true;
437
438 g_free(boollist);
439}
440
441void IniParser::set_int_array(const std::string& group, const std::string& key, const std::vector<int>& value)
442{
443 lock_guard<std::mutex> lock(internal::parser_mutex);
444
445 int count = value.size();
446 gint* intlist = g_new(gint, count);
447
448 for (int i = 0; i < count; ++i)
449 {
450 intlist[i] = value[i];
451 }
452
453 g_key_file_set_integer_list(p->k, group.c_str(), key.c_str(), intlist, count);
454 p->dirty = true;
455
456 g_free(intlist);
457}
458
459void IniParser::set_double_array(const std::string& group, const std::string& key, const std::vector<double>& value)
460{
461 lock_guard<std::mutex> lock(internal::parser_mutex);
462
463 int count = value.size();
464 gdouble* doublelist = g_new(gdouble, count);
465
466 for (int i = 0; i < count; ++i)
467 {
468 doublelist[i] = value[i];
469 }
470
471 g_key_file_set_double_list(p->k, group.c_str(), key.c_str(), doublelist, count);
472 p->dirty = true;
473
474 g_free(doublelist);
475}
476
477void IniParser::sync()
478{
479 lock_guard<std::mutex> lock(internal::parser_mutex);
480
481 if (p->dirty)
482 {
483 GError* e = nullptr;
484 if (!g_key_file_save_to_file(p->k, p->filename.c_str(), &e))
485 {
486 string message = "Could not write ini file ";
487 message += p->filename;
488 message += ": ";
489 message += e->message;
490 int errnum = e->code;
491 g_error_free(e);
492 throw FileException(message, errnum);
493 }
494
495 p->dirty = false;
496 }
497}
267498
268} // namespace util499} // namespace util
269500
270501
=== modified file 'test/data/sample.ini'
--- test/data/sample.ini 2014-07-03 05:00:00 +0000
+++ test/data/sample.ini 2016-03-30 08:50:57 +0000
@@ -1,6 +1,7 @@
1[first]1[first]
22
3intvalue = 13intvalue = 1
4doublevalue = 2.345
4boolvalue = true5boolvalue = true
5stringvalue = hello6stringvalue = hello
6locstring = world7locstring = world
@@ -17,3 +18,4 @@
17boolvalue = false18boolvalue = false
18stringvalue = there19stringvalue = there
19intarray = 4;5;6;78;8;9;9;345;320intarray = 4;5;6;78;8;9;9;345;3
21doublearray = 4.5;6.78;9;10.11
2022
=== modified file 'test/gtest/unity/util/IniParser/CMakeLists.txt'
--- test/gtest/unity/util/IniParser/CMakeLists.txt 2013-06-06 12:02:05 +0000
+++ test/gtest/unity/util/IniParser/CMakeLists.txt 2016-03-30 08:50:57 +0000
@@ -1,4 +1,6 @@
1add_executable(IniParser_test IniParser_test.cpp)1add_executable(IniParser_test IniParser_test.cpp)
2target_link_libraries(IniParser_test ${LIBS} ${TESTLIBS})2target_link_libraries(IniParser_test ${LIBS} ${TESTLIBS})
33
4add_definitions(-DTEST_RUNTIME_PATH="${CMAKE_CURRENT_BINARY_DIR}")
5
4add_test(IniParser IniParser_test)6add_test(IniParser IniParser_test)
57
=== modified file 'test/gtest/unity/util/IniParser/IniParser_test.cpp'
--- test/gtest/unity/util/IniParser/IniParser_test.cpp 2014-07-03 05:00:00 +0000
+++ test/gtest/unity/util/IniParser/IniParser_test.cpp 2016-03-30 08:50:57 +0000
@@ -26,6 +26,7 @@
26using namespace unity::util;26using namespace unity::util;
2727
28#define INI_FILE UNITY_API_TEST_DATADIR "/sample.ini"28#define INI_FILE UNITY_API_TEST_DATADIR "/sample.ini"
29#define INI_TEMP_FILE TEST_RUNTIME_PATH "/temp.ini"
2930
30TEST(IniParser, basic)31TEST(IniParser, basic)
31{32{
@@ -45,11 +46,23 @@
45{46{
46 IniParser conf(INI_FILE);47 IniParser conf(INI_FILE);
4748
48 ASSERT_TRUE(conf.has_group("first"));49 ASSERT_FALSE(conf.has_key("first", "missingvalue"));
49 ASSERT_FALSE(conf.has_group("nonexisting"));50 ASSERT_FALSE(conf.has_group("nonexisting"));
5051
51 ASSERT_TRUE(conf.has_key("first", "stringvalue"));52 ASSERT_TRUE(conf.has_key("first", "stringvalue"));
52 ASSERT_FALSE(conf.has_key("first", "missingvalue"));53 ASSERT_TRUE(conf.has_group("first"));
54
55 ASSERT_TRUE(conf.remove_key("first", "stringvalue"));
56 EXPECT_THROW(conf.remove_key("first", "missingvalue"), LogicException);
57
58 ASSERT_FALSE(conf.has_key("first", "stringvalue"));
59 ASSERT_TRUE(conf.has_group("first"));
60
61 ASSERT_TRUE(conf.remove_group("first"));
62 EXPECT_THROW(conf.remove_group("nonexisting"), LogicException);
63
64 EXPECT_THROW(conf.has_key("first", "stringvalue"), LogicException);
65 ASSERT_FALSE(conf.has_group("first"));
53}66}
5467
55TEST(IniParser, simpleQueries)68TEST(IniParser, simpleQueries)
@@ -64,11 +77,13 @@
64 ASSERT_EQ("second", groups[1]);77 ASSERT_EQ("second", groups[1]);
6578
66 vector<string> firstKeys = conf.get_keys("first");79 vector<string> firstKeys = conf.get_keys("first");
67 ASSERT_EQ(10, firstKeys.size());80 ASSERT_EQ(11, firstKeys.size());
68 ASSERT_EQ("boolvalue", firstKeys[1]);81 ASSERT_EQ("boolvalue", firstKeys[2]);
6982
70 ASSERT_EQ(conf.get_string("first", "stringvalue"), "hello");83 ASSERT_EQ(conf.get_string("first", "stringvalue"), "hello");
71 ASSERT_EQ(1, conf.get_int("first", "intvalue"));84 ASSERT_EQ(1, conf.get_int("first", "intvalue"));
85 ASSERT_EQ(1, conf.get_double("first", "intvalue"));
86 ASSERT_EQ(2.345, conf.get_double("first", "doublevalue"));
72 ASSERT_FALSE(conf.get_boolean("second", "boolvalue"));87 ASSERT_FALSE(conf.get_boolean("second", "boolvalue"));
7388
74 ASSERT_EQ("world", conf.get_string("first", "locstring"));89 ASSERT_EQ("world", conf.get_string("first", "locstring"));
@@ -92,6 +107,18 @@
92 ASSERT_EQ(4, intArr[0]);107 ASSERT_EQ(4, intArr[0]);
93 ASSERT_EQ(3, intArr[8]);108 ASSERT_EQ(3, intArr[8]);
94109
110 vector<double> intToDblArr = conf.get_double_array("second", "intarray");
111 ASSERT_EQ(9, intToDblArr.size());
112 ASSERT_EQ(4, intToDblArr[0]);
113 ASSERT_EQ(3, intToDblArr[8]);
114
115 vector<double> dblArr = conf.get_double_array("second", "doublearray");
116 ASSERT_EQ(4, dblArr.size());
117 ASSERT_EQ(4.5, dblArr[0]);
118 ASSERT_EQ(6.78, dblArr[1]);
119 ASSERT_EQ(9, dblArr[2]);
120 ASSERT_EQ(10.11, dblArr[3]);
121
95 vector<bool> boolArr = conf.get_boolean_array("first", "boolarray");122 vector<bool> boolArr = conf.get_boolean_array("first", "boolarray");
96 ASSERT_EQ(3, boolArr.size());123 ASSERT_EQ(3, boolArr.size());
97 ASSERT_TRUE(boolArr[0]);124 ASSERT_TRUE(boolArr[0]);
@@ -115,36 +142,263 @@
115{142{
116 IniParser conf(INI_FILE);143 IniParser conf(INI_FILE);
117144
118 try {145 try
146 {
119 conf.get_string("foo", "bar");147 conf.get_string("foo", "bar");
120 FAIL();148 }
121 } catch(const LogicException &e) {149 catch (const LogicException& e)
122 }150 {
123 try {151 EXPECT_NE(string::npos, string(e.what()).find("Key file does not have group 'foo'"));
152 }
153 try
154 {
124 conf.get_locale_string("foo", "bar");155 conf.get_locale_string("foo", "bar");
125 FAIL();156 }
126 } catch(const LogicException &e) {157 catch (const LogicException& e)
127 }158 {
128 try {159 EXPECT_NE(string::npos, string(e.what()).find("Key file does not have group 'foo'"));
160 }
161 try
162 {
129 conf.get_int("foo", "bar");163 conf.get_int("foo", "bar");
130 FAIL();164 }
131 } catch(const LogicException &e) {165 catch (const LogicException& e)
132 }166 {
133 try {167 EXPECT_NE(string::npos, string(e.what()).find("Key file does not have group 'foo'"));
168 }
169 try
170 {
171 conf.get_int("first", "doublevalue");
172 }
173 catch (const LogicException& e)
174 {
175 EXPECT_NE(string::npos, string(e.what()).find("Key file contains key 'doublevalue' in group 'first' which has a value that cannot be interpreted."));
176 }
177 try
178 {
134 conf.get_boolean("foo", "bar");179 conf.get_boolean("foo", "bar");
135 FAIL();180 }
136 } catch(const LogicException &e) {181 catch (const LogicException& e)
137 }182 {
138183 EXPECT_NE(string::npos, string(e.what()).find("Key file does not have group 'foo'"));
139 try {184 }
185 try
186 {
140 conf.get_int_array("first", "array");187 conf.get_int_array("first", "array");
141 FAIL();188 }
142 } catch(const LogicException &e) {189 catch (const LogicException& e)
143 }190 {
144191 EXPECT_NE(string::npos, string(e.what()).find("Value 'foo' cannot be interpreted as a number."));
145 try {192 }
193 try
194 {
195 conf.get_int_array("second", "doublearray");
196 }
197 catch (const LogicException& e)
198 {
199 EXPECT_NE(string::npos, string(e.what()).find("Value '4.5' cannot be interpreted as a number."));
200 }
201 try
202 {
146 conf.get_boolean_array("first", "array");203 conf.get_boolean_array("first", "array");
147 FAIL();204 }
148 } catch(const LogicException &e) {205 catch (const LogicException& e)
149 }206 {
207 EXPECT_NE(string::npos, string(e.what()).find("Value 'foo' cannot be interpreted as a boolean."));
208 }
209}
210
211TEST(IniParser, writeValues)
212{
213 // Create an empty ini file for writing
214 auto f = fopen(INI_TEMP_FILE, "w");
215 fclose(f);
216
217 IniParser conf(INI_TEMP_FILE);
218
219 conf.set_string("g1", "k1", "s0;s1");
220 conf.set_locale_string("g1", "k2", "s2", "en");
221 conf.set_locale_string("g1", "k2", "s3", "pt_BR");
222
223 // Check temp file before sync()
224 {
225 IniParser conf2(INI_TEMP_FILE);
226 EXPECT_EQ("s0;s1", conf.get_string("g1", "k1"));
227 EXPECT_THROW(conf2.get_string("g1", "k1"), LogicException);
228
229 EXPECT_EQ("s2", conf.get_locale_string("g1", "k2", "en"));
230 EXPECT_THROW(conf2.get_locale_string("g1", "k2", "en"), LogicException);
231
232 EXPECT_EQ("s3", conf.get_locale_string("g1", "k2", "pt_BR"));
233 EXPECT_THROW(conf2.get_locale_string("g1", "k2", "pt_BR"), LogicException);
234 }
235
236 // Sync
237 conf.sync();
238
239 // Write some post-sync values
240 conf.set_boolean("g2", "k1", false);
241 conf.set_int("g2", "k2", 123);
242 conf.set_double("g3", "k3", 4.56789);
243
244 // Check temp file after first sync()
245 {
246 IniParser conf2(INI_TEMP_FILE);
247 EXPECT_EQ("s0;s1", conf.get_string("g1", "k1"));
248 EXPECT_EQ("s0;s1", conf2.get_string("g1", "k1"));
249
250 EXPECT_EQ("s2", conf.get_locale_string("g1", "k2", "en"));
251 EXPECT_EQ("s2", conf2.get_locale_string("g1", "k2", "en"));
252
253 EXPECT_EQ("s3", conf.get_locale_string("g1", "k2", "pt_BR"));
254 EXPECT_EQ("s3", conf2.get_locale_string("g1", "k2", "pt_BR"));
255
256 EXPECT_EQ(false, conf.get_boolean("g2", "k1"));
257 EXPECT_THROW(conf2.get_boolean("g2", "k1"), LogicException);
258
259 EXPECT_EQ(123, conf.get_int("g2", "k2"));
260 EXPECT_THROW(conf2.get_int("g2", "k2"), LogicException);
261
262 EXPECT_EQ(4.56789, conf.get_double("g3", "k3"));
263 EXPECT_THROW(conf2.get_double("g3", "k3"), LogicException);
264 }
265
266 // Sync
267 conf.sync();
268
269 // Check temp file after second sync()
270 {
271 IniParser conf2(INI_TEMP_FILE);
272 EXPECT_EQ("s0;s1", conf.get_string("g1", "k1"));
273 EXPECT_EQ("s0;s1", conf2.get_string("g1", "k1"));
274
275 EXPECT_EQ("s2", conf.get_locale_string("g1", "k2", "en"));
276 EXPECT_EQ("s2", conf2.get_locale_string("g1", "k2", "en"));
277
278 EXPECT_EQ("s3", conf.get_locale_string("g1", "k2", "pt_BR"));
279 EXPECT_EQ("s3", conf2.get_locale_string("g1", "k2", "pt_BR"));
280
281 EXPECT_EQ(false, conf.get_boolean("g2", "k1"));
282 EXPECT_EQ(false, conf2.get_boolean("g2", "k1"));
283
284 EXPECT_EQ(123, conf.get_int("g2", "k2"));
285 EXPECT_EQ(123, conf2.get_int("g2", "k2"));
286
287 EXPECT_EQ(4.56789, conf.get_double("g3", "k3"));
288 EXPECT_EQ(4.56789, conf2.get_double("g3", "k3"));
289 }
290}
291
292template<class T>
293void EXPECT_ARRAY_EQ(const vector<T>& expected, const vector<T>& actual)
294{
295 EXPECT_TRUE(expected == actual);
296}
297
298TEST(IniParser, writeArrays)
299{
300 // Create an empty ini file for writing
301 auto f = fopen(INI_TEMP_FILE, "w");
302 fclose(f);
303
304 IniParser conf(INI_TEMP_FILE);
305
306 conf.set_string_array("g1", "k1", {"s0;s1", "s2", "s3"});
307 conf.set_locale_string_array("g1", "k2", {"s4", "s5", "s6"}, "en");
308 conf.set_locale_string_array("g1", "k2", {"s7", "s8", "s9=s10"}, "pt_BR");
309
310 // Check temp file before sync()
311 {
312 IniParser conf2(INI_TEMP_FILE);
313 EXPECT_ARRAY_EQ({"s0;s1", "s2", "s3"}, conf.get_string_array("g1", "k1"));
314 EXPECT_THROW(conf2.get_string_array("g1", "k1"), LogicException);
315
316 EXPECT_ARRAY_EQ({"s4", "s5", "s6"}, conf.get_locale_string_array("g1", "k2", "en"));
317 EXPECT_THROW(conf2.get_locale_string_array("g1", "k2", "en"), LogicException);
318
319 EXPECT_ARRAY_EQ({"s7", "s8", "s9=s10"}, conf.get_locale_string_array("g1", "k2", "pt_BR"));
320 EXPECT_THROW(conf2.get_locale_string_array("g1", "k2", "pt_BR"), LogicException);
321 }
322
323 // Sync
324 conf.sync();
325
326 // Write some post-sync values
327 conf.set_boolean_array("g2", "k1", {true, false, false});
328 conf.set_int_array("g2", "k2", {123, 456789, 101112});
329 conf.set_double_array("g2", "k3", {13.14, 15.161718, 19.2});
330
331 // Check temp file after first sync()
332 {
333 IniParser conf2(INI_TEMP_FILE);
334 EXPECT_ARRAY_EQ({"s0;s1", "s2", "s3"}, conf.get_string_array("g1", "k1"));
335 EXPECT_ARRAY_EQ({"s0;s1", "s2", "s3"}, conf2.get_string_array("g1", "k1"));
336
337 EXPECT_ARRAY_EQ({"s4", "s5", "s6"}, conf.get_locale_string_array("g1", "k2", "en"));
338 EXPECT_ARRAY_EQ({"s4", "s5", "s6"}, conf2.get_locale_string_array("g1", "k2", "en"));
339
340 EXPECT_ARRAY_EQ({"s7", "s8", "s9=s10"}, conf.get_locale_string_array("g1", "k2", "pt_BR"));
341 EXPECT_ARRAY_EQ({"s7", "s8", "s9=s10"}, conf2.get_locale_string_array("g1", "k2", "pt_BR"));
342
343 EXPECT_ARRAY_EQ({true, false, false}, conf.get_boolean_array("g2", "k1"));
344 EXPECT_THROW(conf2.get_boolean_array("g2", "k1"), LogicException);
345
346 EXPECT_ARRAY_EQ({123, 456789, 101112}, conf.get_int_array("g2", "k2"));
347 EXPECT_THROW(conf2.get_int_array("g2", "k2"), LogicException);
348
349 EXPECT_ARRAY_EQ({13.14, 15.161718, 19.2}, conf.get_double_array("g2", "k3"));
350 EXPECT_THROW(conf2.get_double_array("g2", "k3"), LogicException);
351 }
352
353 // Sync
354 conf.sync();
355
356 // Check temp file after second sync()
357 {
358 IniParser conf2(INI_TEMP_FILE);
359 EXPECT_ARRAY_EQ({"s0;s1", "s2", "s3"}, conf.get_string_array("g1", "k1"));
360 EXPECT_ARRAY_EQ({"s0;s1", "s2", "s3"}, conf2.get_string_array("g1", "k1"));
361
362 EXPECT_ARRAY_EQ({"s4", "s5", "s6"}, conf.get_locale_string_array("g1", "k2", "en"));
363 EXPECT_ARRAY_EQ({"s4", "s5", "s6"}, conf2.get_locale_string_array("g1", "k2", "en"));
364
365 EXPECT_ARRAY_EQ({"s7", "s8", "s9=s10"}, conf.get_locale_string_array("g1", "k2", "pt_BR"));
366 EXPECT_ARRAY_EQ({"s7", "s8", "s9=s10"}, conf2.get_locale_string_array("g1", "k2", "pt_BR"));
367
368 EXPECT_ARRAY_EQ({true, false, false}, conf.get_boolean_array("g2", "k1"));
369 EXPECT_ARRAY_EQ({true, false, false}, conf2.get_boolean_array("g2", "k1"));
370
371 EXPECT_ARRAY_EQ({123, 456789, 101112}, conf.get_int_array("g2", "k2"));
372 EXPECT_ARRAY_EQ({123, 456789, 101112}, conf2.get_int_array("g2", "k2"));
373
374 EXPECT_ARRAY_EQ({13.14, 15.161718, 19.2}, conf.get_double_array("g2", "k3"));
375 EXPECT_ARRAY_EQ({13.14, 15.161718, 19.2}, conf2.get_double_array("g2", "k3"));
376 }
377}
378
379TEST(IniParser, writeError)
380{
381 // Create an empty ini file for writing
382 auto f = fopen(INI_TEMP_FILE, "w");
383 fclose(f);
384
385 IniParser conf(INI_TEMP_FILE);
386
387 // Replace ini file with a directory
388 ASSERT_EQ(0, remove(INI_TEMP_FILE));
389 ASSERT_EQ(0, mkdir(INI_TEMP_FILE, 0700));
390
391 std::shared_ptr<void> rmdir_raii(nullptr, [](void*)
392 {
393 rmdir(INI_TEMP_FILE);
394 });
395
396 // Sync (no exception as config is not dirty)
397 EXPECT_NO_THROW(conf.sync());
398
399 // Dirty the config
400 conf.set_boolean("g1", "k1", true);
401
402 // Sync (exception as target is a directory)
403 EXPECT_THROW(conf.sync(), FileException);
150}404}

Subscribers

People subscribed via source and target branches

to all changes: