1/*
2 * Copyright (c) 2015, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation and/or
13 * other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "ParameterFramework.h"
32
33#include "TmpFile.hpp"
34
35#define CATCH_CONFIG_MAIN // This tells Catch to provide a main()
36#include <catch.hpp>
37
38#include <string>
39#include <memory>
40#include <vector>
41#include <array>
42
43#include <cstring>
44#include <cerrno>
45#include <climits>
46
47struct Test
48{
49    /** @return true if str is empty. */
50    bool empty(const char *str)
51    {
52        REQUIRE(str != NULL);
53        return *str == '\0';
54    }
55
56    void REQUIRE_FAILURE(bool success)
57    {
58        THEN ("It should be an error") {
59            INFO("Previous pfw log: \n" + logLines);
60            CAPTURE(pfwGetLastError(pfw));
61            CHECK(not success);
62            CHECK(not empty(pfwGetLastError(pfw)));
63        }
64    }
65
66    void REQUIRE_SUCCESS(bool success)
67    {
68        THEN ("It should be a success") {
69            INFO("Previous pfw log: \n" + logLines);
70            CAPTURE(pfwGetLastError(pfw));
71            CHECK(success);
72            CHECK(empty(pfwGetLastError(pfw)));
73        }
74    }
75
76    /** Wrap utility::TmpFile to add an implicit convertion to the temporary file.
77     *
78     * This avoids dozens of .getPath() in the following tests. */
79    class TmpFile : private parameterFramework::utility::TmpFile
80    {
81    private:
82        using Base = parameterFramework::utility::TmpFile;
83
84    public:
85        using Base::TmpFile;
86
87        using Base::getPath;
88        /** Implicitly convert to the path of the temporary file. */
89        operator const char *() const { return getPath().c_str(); }
90    };
91
92    /** Log in logLines. */
93    static void logCb(void *voidLogLines, PfwLogLevel level, const char *logLine)
94    {
95        std::string &logLines = *reinterpret_cast<std::string *>(voidLogLines);
96        switch (level) {
97        case pfwLogWarning:
98            logLines += "Warning: ";
99            break;
100        case pfwLogInfo:
101            logLines += "Info: ";
102        }
103        logLines += logLine;
104        logLines += '\n';
105    }
106
107    /** Log buffer, will only be display in case of failure */
108    std::string logLines;
109
110    /** Pfw handler used in the tests. */
111    PfwHandler *pfw;
112};
113
114TEST_CASE_METHOD(Test, "Parameter-framework c api use")
115{
116    // Create criteria
117    const char *letterList[] = {"a", "b", "c", NULL};
118    const char *numberList[] = {"1", "2", "3", NULL};
119    const PfwCriterion criteria[] = {
120        {"inclusiveCrit", true, letterList}, {"exclusiveCrit", false, numberList},
121    };
122    size_t criterionNb = sizeof(criteria) / sizeof(criteria[0]);
123    PfwLogger logger = {&logLines, logCb};
124
125    // Create valid pfw config file
126    const char *intParameterPath = "/test/system/integer";
127    const char *stringParameterPath = "/test/system/string";
128    TmpFile system("<?xml version='1.0' encoding='UTF-8'?>\
129        <Subsystem Name='system' Type='Virtual'>\
130            <ComponentLibrary/>\
131            <InstanceDefinition>\
132                <IntegerParameter Name='integer' Size='32' Signed='true' Max='100'/>\
133                <StringParameter Name='string' MaxLength='9'/>\
134            </InstanceDefinition>\
135        </Subsystem>");
136    TmpFile libraries("<?xml version='1.0' encoding='UTF-8'?>\
137        <SystemClass Name='test'>\
138            <SubsystemInclude Path='" +
139                      system.getPath() + "'/>\
140        </SystemClass>");
141    TmpFile config("<?xml version='1.0' encoding='UTF-8'?>\
142        <ParameterFrameworkConfiguration\
143            SystemClassName='test' TuningAllowed='false'>\
144            <SubsystemPlugins/>\
145            <StructureDescriptionFileLocation Path='" +
146                   libraries.getPath() + "'/>\
147        </ParameterFrameworkConfiguration>");
148
149    GIVEN ("A created parameter framework") {
150        pfw = pfwCreate();
151        REQUIRE(pfw != NULL);
152
153        THEN ("Error message should be empty") {
154            CHECK(empty(pfwGetLastError(pfw)));
155        }
156
157        WHEN ("The pfw is started without an existent file") {
158            REQUIRE_FAILURE(pfwStart(pfw, "/doNotExist", criteria, criterionNb, &logger));
159        }
160
161        WHEN ("The pfw is started with duplicated criterion value") {
162            const PfwCriterion duplicatedCriteria[] = {
163                {"duplicated name", true, letterList}, {"duplicated name", false, numberList},
164            };
165            REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 2, &logger));
166        }
167        WHEN ("The pfw is started with duplicated criterion value state") {
168            const char *values[] = {"a", "a", NULL};
169            const PfwCriterion duplicatedCriteria[] = {{"name", true, values}};
170
171            WHEN ("Using test logger") {
172                REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, &logger));
173            }
174            WHEN ("Using default logger") {
175                // Test coverage of default logger warning
176                REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, NULL));
177            }
178        }
179        WHEN ("The pfw is started with NULL name criterion") {
180            const PfwCriterion duplicatedCriteria[] = {{NULL, true, letterList}};
181            REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, &logger));
182        }
183        WHEN ("The pfw is started with NULL criterion state list") {
184            const PfwCriterion duplicatedCriteria[] = {{"name", true, NULL}};
185            REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, &logger));
186        }
187        GIVEN ("A criteria with lots of values") {
188            // Build a criterion with as many value as there is bits in int.
189            std::vector<char> names(sizeof(int) * CHAR_BIT + 1, 'a');
190            names.back() = '\0';
191            std::vector<const char *> values(names.size());
192            for (size_t i = 0; i < values.size(); ++i) {
193                values[i] = &names[i];
194            }
195            values.back() = NULL;
196            /* The pfw c api requires criterion values to be a NULL terminated
197             * array of string. Each string is a pointer to a NULL terminated
198             * array of char. The pfw requires each string to be different
199             * from all others, ie strcmp(values[i], values[j]) != 0 for any
200             * i j.
201             *
202             * In order to generate easily an array of different strings,
203             * instantiate one string (names) big enough
204             * (@see PfwCriterion::values).
205             * Then instantiate an array of pointer (values),
206             * each pointing to a different position in the previously
207             * created string.
208             *
209             * Representation of the names and values vectors.
210             *
211             * n = number of bit in an int
212             *            <--- n+1 elements --->
213             * names    = |a|a|a|a|...|a|a|a|\0|
214             *             ^ ^             ^
215             * values[0] = ´ |             |
216             * values[1] = --´             |
217             * ...                         |
218             * values[n - 1] =  -----------´
219             * values[n] = NULL
220             *
221             */
222            const PfwCriterion duplicatedCriteria[] = {{"name", true, &values[0]}};
223
224            WHEN ("The pfw is started with a too long criterion state list") {
225                REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, &logger));
226            }
227            WHEN ("The pfw is started with max length criterion state list") {
228                values[values.size() - 2] = NULL; // Hide last value
229                REQUIRE_SUCCESS(pfwStart(pfw, config, duplicatedCriteria, 1, &logger));
230            }
231        }
232
233        WHEN ("The pfw is started with zero criteria") {
234            REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, 0, &logger));
235        }
236
237        WHEN ("The pfw is started twice a pfw") {
238            REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, criterionNb, &logger));
239            REQUIRE_FAILURE(pfwStart(pfw, config, criteria, criterionNb, &logger));
240        }
241
242        WHEN ("The pfw is started without a logger callback") {
243            PfwLogger noLog = {NULL, NULL};
244            REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, criterionNb, &noLog));
245        }
246        WHEN ("The pfw is started with default logger") {
247            REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, criterionNb, NULL));
248        }
249
250        WHEN ("Get criterion of a stopped pfw") {
251            int value;
252            REQUIRE_FAILURE(pfwGetCriterion(pfw, criteria[0].name, &value));
253        }
254        WHEN ("Set criterion of a stopped pfw") {
255            REQUIRE_FAILURE(pfwSetCriterion(pfw, criteria[0].name, 1));
256        }
257        WHEN ("Commit criteria of a stopped pfw") {
258            REQUIRE_FAILURE(pfwApplyConfigurations(pfw));
259        }
260
261        WHEN ("Bind parameter with a stopped pfw") {
262            REQUIRE(pfwBindParameter(pfw, intParameterPath) == NULL);
263        }
264
265        WHEN ("The pfw is started correctly") {
266            REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, criterionNb, &logger));
267            int value;
268
269            WHEN ("Get not existing criterion") {
270                REQUIRE_FAILURE(pfwGetCriterion(pfw, "Do not exist", &value));
271            }
272            THEN ("All criterion should value 0") {
273                for (size_t i = 0; i < criterionNb; ++i) {
274                    const char *criterionName = criteria[i].name;
275                    CAPTURE(criterionName);
276                    REQUIRE_SUCCESS(pfwGetCriterion(pfw, criterionName, &value));
277                    REQUIRE(value == 0);
278                }
279            }
280
281            WHEN ("Set not existing criterion") {
282                REQUIRE_FAILURE(pfwSetCriterion(pfw, "Do not exist", 3));
283            }
284            WHEN ("Set criterion value") {
285                for (size_t i = 0; i < criterionNb; ++i) {
286                    const char *criterionName = criteria[i].name;
287                    CAPTURE(criterionName);
288                    REQUIRE_SUCCESS(pfwSetCriterion(pfw, criterionName, 3));
289                }
290                THEN ("Get criterion value should return what was set") {
291                    for (size_t i = 0; i < criterionNb; ++i) {
292                        const char *criterionName = criteria[i].name;
293                        CAPTURE(criterionName);
294                        REQUIRE_SUCCESS(pfwGetCriterion(pfw, criterionName, &value));
295                        REQUIRE(value == 3);
296                    }
297                }
298                WHEN ("Set a new value to a criterion without committing first") {
299                    const char *criterionName = criteria[0].name;
300                    REQUIRE_SUCCESS(pfwSetCriterion(pfw, criterionName, 0));
301                    THEN ("A warning message should have been displayed") {
302                        INFO("Previous pfw log: \n" + logLines);
303                        size_t logPos = logLines.find("Warning: Selection criterion "
304                                                      "'inclusiveCrit' has been modified 1 time(s)"
305                                                      " without any configuration application");
306                        CHECK(logPos != std::string::npos);
307                    }
308                }
309            }
310            WHEN ("Commit criteria of a started pfw") {
311                REQUIRE_SUCCESS(pfwApplyConfigurations(pfw));
312            }
313            WHEN ("Bind a non existing parameter") {
314                REQUIRE_FAILURE(pfwBindParameter(pfw, "do/not/exist") != NULL);
315            }
316
317            GIVEN ("An integer parameter handle") {
318                PfwParameterHandler *param = pfwBindParameter(pfw, intParameterPath);
319                REQUIRE_SUCCESS(param != NULL);
320
321                WHEN ("Set parameter out of range") {
322                    REQUIRE_FAILURE(pfwSetIntParameter(param, 101));
323                }
324
325                WHEN ("Set parameter") {
326                    REQUIRE_SUCCESS(pfwSetIntParameter(param, 11));
327                    THEN ("Get parameter should return what was set") {
328                        REQUIRE_SUCCESS(pfwGetIntParameter(param, &value));
329                        REQUIRE(value == 11);
330                    }
331                }
332
333                pfwUnbindParameter(param);
334            }
335
336            GIVEN ("An string parameter handle") {
337                PfwParameterHandler *param = pfwBindParameter(pfw, stringParameterPath);
338                REQUIRE_SUCCESS(param != NULL);
339
340                WHEN ("Set parameter out of range") {
341                    REQUIRE_FAILURE(pfwSetStringParameter(param, "ko_1234567"));
342                }
343
344                WHEN ("Set parameter") {
345                    char *value;
346                    REQUIRE_SUCCESS(pfwSetStringParameter(param, "ok"));
347                    THEN ("Get parameter should return what was set") {
348                        REQUIRE_SUCCESS(pfwGetStringParameter(param, &value));
349                        REQUIRE(value == std::string("ok"));
350                        pfwFree(value);
351                    }
352                }
353
354                pfwUnbindParameter(param);
355            }
356        }
357
358        pfwDestroy(pfw);
359    }
360}
361