From 87fcc58c5397fbff6256302bbb866bac547f4f0b Mon Sep 17 00:00:00 2001 From: David Wagner Date: Tue, 7 Jul 2015 15:43:53 +0200 Subject: [PATCH 01/15] Client Simulator: remove some manual path concatenations Some paths were concatenated by inserting a "/" between the two components. This is not portable and also makes it impossible to handle absolute paths. Instead, os.path.join is both portable and "smart" about absolute paths: os.path.join("foo/bar", "/spam") == "/spam" Signed-off-by: David Wagner --- .../clientsimulator/testGenerator/TestLauncher.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py index 6cd0c47d9..c9d65fc2f 100644 --- a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py +++ b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py @@ -90,7 +90,7 @@ def __init__(self, # Command used to generate coverage self.__coverageCmd = [ "eval", - configParser["CoverageDir"] + "/aplog2coverage.sh", + os.path.join(configParser["CoverageDir"], "aplog2coverage.sh"), "-d", configParser["PfwDomainConfFile"], "-e.", @@ -175,9 +175,10 @@ def executeScript(self, scriptName): launchType = self.__availableLaunchType[0] # Create and launch the command to use the desired script + # A script's path is absolute or relative to the "ScriptsFile" file. self.__call_process( - ["eval", "{}/{}".format( - os.path.split(self.__configParser["ScriptsFile"])[0], + ["eval", os.path.join( + os.path.dirname(self.__configParser["ScriptsFile"]), script)], launchType == self.__availableLaunchType[0], True) From 51f5016214e0df4907c124d16999ffdf14fd87b6 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Tue, 7 Jul 2015 15:44:48 +0200 Subject: [PATCH 02/15] Client Simulator: remove unused TestVector class This simply was dead code. Signed-off-by: David Wagner --- .../testGenerator/TestVector.py | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 tools/clientSimulator/clientsimulator/testGenerator/TestVector.py diff --git a/tools/clientSimulator/clientsimulator/testGenerator/TestVector.py b/tools/clientSimulator/clientsimulator/testGenerator/TestVector.py deleted file mode 100644 index 97663870f..000000000 --- a/tools/clientSimulator/clientsimulator/testGenerator/TestVector.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2014-2015, Intel Corporation -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation and/or -# other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -class TestVector: - - """ This class describe a test vector which can be launched by a TestLauncher object """ - - def __init__(self, name, criterions, testType): - self.__name = name - self.__testType = testType - self.__criterions = criterions - - @property - def criterions(self): - return self.__criterions - - @property - def testType(self): - return self.__testType - - @testType.setter - def testType(self, value): - self.__testType = value - - def __str__(self): - toString = "Test Type : {}\n".format(self.__testType) - for criterion in self.__criterions: - toString += (str(criterion) + '\n') - return toString - - -class InvalidTestTypeValueException(Exception): - - """ Exception raised in case of problem with the test type """ - - def __init__(self, msg): - self.__msg = msg - - def __str__(self): - return "Invalid Test Type Error : " + self.__msg From 4820145244e5a5993fd866973c705c32de572eea Mon Sep 17 00:00:00 2001 From: David Wagner Date: Tue, 7 Jul 2015 15:45:46 +0200 Subject: [PATCH 03/15] Client Simulator: remove unused imports They were dead code. Signed-off-by: David Wagner --- tools/clientSimulator/clientsimulator/scenario/Scenario.py | 1 - tools/clientSimulator/pfClientSimulator.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/tools/clientSimulator/clientsimulator/scenario/Scenario.py b/tools/clientSimulator/clientsimulator/scenario/Scenario.py index 6284eba56..86b529e5f 100644 --- a/tools/clientSimulator/clientsimulator/scenario/Scenario.py +++ b/tools/clientSimulator/clientsimulator/scenario/Scenario.py @@ -28,7 +28,6 @@ import json import logging -import os class Scenario: diff --git a/tools/clientSimulator/pfClientSimulator.py b/tools/clientSimulator/pfClientSimulator.py index 28d080f65..d039d8954 100755 --- a/tools/clientSimulator/pfClientSimulator.py +++ b/tools/clientSimulator/pfClientSimulator.py @@ -37,8 +37,6 @@ from clientsimulator.userInteraction.UserInteractor import UserInteractor from clientsimulator.userInteraction.DynamicCallHelper import DynamicCallHelper import argparse -import threading -import signal import time import logging import os From 725560ae969921f8ad8aa78349d745caa4d46386 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Tue, 7 Jul 2015 17:05:08 +0200 Subject: [PATCH 04/15] Client Simulator: reduce the scope of a try..except block Such unecessarily wide blocks make the code harder to read. Signed-off-by: David Wagner --- .../clientsimulator/scenario/Scenario.py | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/tools/clientSimulator/clientsimulator/scenario/Scenario.py b/tools/clientSimulator/clientsimulator/scenario/Scenario.py index 86b529e5f..a14a2483e 100644 --- a/tools/clientSimulator/clientsimulator/scenario/Scenario.py +++ b/tools/clientSimulator/clientsimulator/scenario/Scenario.py @@ -103,26 +103,13 @@ def __parseScenarioActions(self, scenarioFileName, actionGathererFileName): scenarioGatheredActions = json.load(actionGathererFile) for action in scenarioActions: + actionDefinedType = self.__getActionType(action) + if actionDefinedType in self.__actionTypeBehaviour.keys(): + continue + try: - actionDefinedType = self.__getActionType(action) - if actionDefinedType not in self.__actionTypeBehaviour.keys(): - actionValue = action.pop(actionDefinedType) - actionGatherer = scenarioGatheredActions[actionDefinedType] - - if self.__getActionType(actionGatherer) == "script": - raise UngatherableTypeException( - "Unable to redefine {} type, please edit your {} file".format( - self.__getActionType(actionGatherer), - actionGathererFileName)) - - # Fusion of gathered Actions and other desired actions which - # are directly writed in the scenario's file - actionValue.update( - self.__getActionValue(actionGatherer)) - - # Change the user defined key which was previously popped - # by the known one - action[self.__getActionType(actionGatherer)] = actionValue + actionValue = action.pop(actionDefinedType) + actionGatherer = scenarioGatheredActions[actionDefinedType] except KeyError as e: self.__logger.error( "Actions {} from {} file is not valid".format( @@ -130,6 +117,20 @@ def __parseScenarioActions(self, scenarioFileName, actionGathererFileName): scenarioFileName)) raise e + if self.__getActionType(actionGatherer) == "script": + raise UngatherableTypeException( + "Unable to redefine {} type, please edit your {} file".format( + self.__getActionType(actionGatherer), + actionGathererFileName)) + + # Fusion of gathered Actions and other desired actions which + # are directly writed in the scenario's file + actionValue.update(self.__getActionValue(actionGatherer)) + + # Change the user defined key which was previously popped + # by the known one + action[self.__getActionType(actionGatherer)] = actionValue + return scenarioActions def __getActionType(self, action): From b025c96888b433299b57a042fd568dc3dce29419 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Tue, 7 Jul 2015 17:06:34 +0200 Subject: [PATCH 05/15] Client Simulator: remove a useless list-comprehension usage The sorted() function already returns a strict list (i.e. not a generator or other such lazy iterable). Thus this identity list-comprehension: [i for i in sorted(x)] is useless and can be rewritten as: sorted(x) Signed-off-by: David Wagner --- tools/clientSimulator/pfClientSimulator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/clientSimulator/pfClientSimulator.py b/tools/clientSimulator/pfClientSimulator.py index d039d8954..05cbbc6b6 100755 --- a/tools/clientSimulator/pfClientSimulator.py +++ b/tools/clientSimulator/pfClientSimulator.py @@ -188,8 +188,7 @@ def main(): testLauncher )) for scenarioNumber, scenarioFileName in enumerate( - [file for file in sorted(os.listdir( - configParser["ScenariosDirectory"]))]) + sorted(os.listdir(configParser["ScenariosDirectory"]))) } if args.scenario is not None: scenarioOptions[args.scenario][1]() From 3af82735e5aebbfcf6487d5856bca52c4e85a46c Mon Sep 17 00:00:00 2001 From: David Wagner Date: Wed, 8 Jul 2015 11:26:45 +0200 Subject: [PATCH 06/15] Client Simulator: simplify the main loop And avoid modifying UserInteractor.getMenu's input argument. Signed-off-by: David Wagner --- .../userInteraction/UserInteractor.py | 25 +++++++---- tools/clientSimulator/pfClientSimulator.py | 44 +++++++++---------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/tools/clientSimulator/clientsimulator/userInteraction/UserInteractor.py b/tools/clientSimulator/clientsimulator/userInteraction/UserInteractor.py index df7833f11..d481b61c9 100644 --- a/tools/clientSimulator/clientsimulator/userInteraction/UserInteractor.py +++ b/tools/clientSimulator/clientsimulator/userInteraction/UserInteractor.py @@ -51,28 +51,35 @@ def __init__(self, testLauncher, criterions): self.__criterions = criterions @classmethod - def getMenu(cls, options): + def getMenu(cls, options, cancelSentence="Go Back"): """ Dynamic Menu Generator : :param options: dictionnary containing, the invite string and the function to launch :type options: dict + :param cancelSentence: title of the menu entry that will be + appended after the provided options, in order to exit the menu. For + top-level menus, it is advised to pass "Quit" as argument. + :type cancelSentence: string """ - testQuit = True - - options[len(options)] = \ - ("Go Back", lambda: False) - while testQuit: + while True: print("\nPlease Make a choice : ") for numMenu, (sentenceMenu, fonc) in sorted(options.items()): print("\t{}. {}".format(numMenu, sentenceMenu)) - choice = input("Your Choice : ") + # Lastly, append an option to go to the previous menu/quit + print("\t{}. {}".format(len(options), cancelSentence)) + choice = input("Your Choice : ") try: - testQuit = options[int(choice)][1]() + choice = int(choice) + if choice == len(options): + # The user has selected the "cancel" option + break + + options[choice][1]() except (KeyError, ValueError) as e: print("Invalid Choice : {}".format(e)) @@ -87,7 +94,7 @@ def launchInteractiveMode(self): 2: ("Launch Script", self.__launchScript) } - UserInteractor.getMenu(optionsMenu) + UserInteractor.getMenu(optionsMenu, "Quit") def __applyConfiguration(self): """ diff --git a/tools/clientSimulator/pfClientSimulator.py b/tools/clientSimulator/pfClientSimulator.py index 05cbbc6b6..caa3dfb72 100755 --- a/tools/clientSimulator/pfClientSimulator.py +++ b/tools/clientSimulator/pfClientSimulator.py @@ -173,29 +173,27 @@ def main(): testLauncher, testFactory.generateTestVector()).launchInteractiveMode() else: - while True: - scenarioOptions = { - scenarioNumber: - (scenarioFileName, - DynamicCallHelper( - launchScenario, - logger, - consoleLogger, - configParser["ActionGathererFile"], - os.path.join( - configParser["ScenariosDirectory"], scenarioFileName), - testFactory, - testLauncher - )) - for scenarioNumber, scenarioFileName in enumerate( - sorted(os.listdir(configParser["ScenariosDirectory"]))) - } - if args.scenario is not None: - scenarioOptions[args.scenario][1]() - # Let the user choose other scenario after the one choosed by command line - args.scenario = None - else: - UserInteractor.getMenu(scenarioOptions) + scenarioOptions = { + scenarioNumber: + (scenarioFileName, + DynamicCallHelper( + launchScenario, + logger, + consoleLogger, + configParser["ActionGathererFile"], + os.path.join( + configParser["ScenariosDirectory"], scenarioFileName), + testFactory, + testLauncher + )) + for scenarioNumber, scenarioFileName in enumerate( + sorted(os.listdir(configParser["ScenariosDirectory"]))) + } + if args.scenario is not None: + scenarioOptions[args.scenario][1]() + # Let the user choose more scenarios after the one chosen by command line + # or if none was given on the command line. + UserInteractor.getMenu(scenarioOptions, "Quit") except KeyboardInterrupt as e: close(logger, testLauncher, args.coverage) From a478cf67a11e518738ca45206e940835ef7c5d66 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Wed, 8 Jul 2015 11:29:17 +0200 Subject: [PATCH 07/15] Client Simulator: Replace a bare string statement by a comment A bare string statement at the beginning of a class or a function is evaluated as a docstring but it makes no sense at the beginning of an if-clause. Change it to a comment instead. Signed-off-by: David Wagner --- tools/clientSimulator/pfClientSimulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/clientSimulator/pfClientSimulator.py b/tools/clientSimulator/pfClientSimulator.py index caa3dfb72..82cbedef6 100755 --- a/tools/clientSimulator/pfClientSimulator.py +++ b/tools/clientSimulator/pfClientSimulator.py @@ -199,5 +199,5 @@ def main(): if __name__ == "__main__": - """ Execute main if the script is running as main """ + # Execute main if the script is running as main main() From 7abcd517bdfbf5301df1e28f340f8f11338a78ca Mon Sep 17 00:00:00 2001 From: David Wagner Date: Thu, 9 Jul 2015 10:21:24 +0200 Subject: [PATCH 08/15] Client Simulator: remove the tools' paths from the config items remote-process and test-platform should simply be in the PATH and so, there is no need for their paths to be put in conf.json. This change is somewhat backward-compatible because these configuration items will simply be ignored. Another reason why it wasn't very useful for these paths to be in conf.json is that, even if their path is know, one still has to add their library's path to LD_LIBRARY_PATH. It is easier to install the Parameter Framework project, in order to have everything set. Signed-off-by: David Wagner --- tools/clientSimulator/README.md | 8 +++----- .../clientsimulator/testGenerator/TestLauncher.py | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tools/clientSimulator/README.md b/tools/clientSimulator/README.md index 2a7c9b6bc..34306370e 100644 --- a/tools/clientSimulator/README.md +++ b/tools/clientSimulator/README.md @@ -40,7 +40,9 @@ configuration functional tests. ## How to run tests -You can run tests using pfClientSimulator.py. +You can run tests using pfClientSimulator.py. `test-platform` and +`remote-process` need to be in the PATH (e.g. by installing the Parameter +Framework - see the main README file). You have to run the script with at least the test directory argument: @@ -82,8 +84,6 @@ A test directory should contains a `conf.json` file containing: (see below). - The absolute path to the log output file (optional). - A setup script (inline shell) (optional). -- The absolute paths to test-platform and remote-process executables (or the - executables' names if they are in the PATH). - The host and port on which the test-platform and the Parameter Framework instance are listening for commands. - The absolute path to the directory containing the coverage generation tool @@ -118,8 +118,6 @@ working directory*. "SetupScript" : "echo 'bouh'", - "TestPlatformCommand" : "test-platform", - "RemoteProcessCommand" : "remote-process", "TestPlatformHost" : "localhost 5001", "ParameterFramworkHost" : "localhost 5000", diff --git a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py index c9d65fc2f..46096fd5e 100644 --- a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py +++ b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py @@ -58,17 +58,17 @@ def __init__(self, self.__configParser = configParser # Prepare basic commands - halCommand = [configParser["RemoteProcessCommand"], + halCommand = ["remote-process", configParser["TestPlatformHost"]] setCriteriaCommand = halCommand + ["setCriterionState"] - testPlatformHostCommand = [configParser["RemoteProcessCommand"], + testPlatformHostCommand = ["remote-process", configParser["TestPlatformHost"]] self.__logFileName = configParser["LogFile"] # Commands self.__startTestPlatformCmd = [configParser["PrefixCommand"], - configParser["TestPlatformCommand"], + "test-platform", configParser["PfwConfFile"]] self.__createCriterionCmd = [configParser["PrefixCommand"]] From a781e614fefe2d2bd6985293e4017e21d7d88976 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Thu, 9 Jul 2015 10:27:54 +0200 Subject: [PATCH 09/15] Client Simulator/conf.json: no need to mention the remote-command handlers host The configuratio item "TestPlatformHost" is replaced by "TestPlatformPort" while "ParameterFramworkHost" is completely removed (it wasn't used). It made no sense to specify the host since it couldn't have been anything else than "localhost". This change isn't backward-compatible since configuration files need to be changed. Signed-off-by: David Wagner --- tools/clientSimulator/README.md | 7 ++----- .../clientsimulator/testGenerator/TestLauncher.py | 9 ++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/clientSimulator/README.md b/tools/clientSimulator/README.md index 34306370e..c1da90ec6 100644 --- a/tools/clientSimulator/README.md +++ b/tools/clientSimulator/README.md @@ -75,6 +75,7 @@ A test directory should contains a `conf.json` file containing: - The desired command prefix (optional; e.g. `adb shell` in order to execute tests on an android board or empty to execute locally). +- The port on which the test-platform should be started. - The criterion file path (see [this README](https://github.com/01org/parameter-framework/tree/master/tools/xmlGenerator#domaingeneratorpy)). - The absolute path to the Parameter Framework toplevel configuration file. @@ -84,8 +85,6 @@ A test directory should contains a `conf.json` file containing: (see below). - The absolute path to the log output file (optional). - A setup script (inline shell) (optional). -- The host and port on which the test-platform and the Parameter Framework - instance are listening for commands. - The absolute path to the directory containing the coverage generation tool (optional; for coverage only). - The path to the html coverage output file (optional; for coverage only). @@ -106,6 +105,7 @@ working directory*. ```{.json} { "PrefixCommand" : "adb shell", + "TestPlatformPort" : "5001", "CriterionFile" : "MyCriteria.txt", "PfwConfFile" : "/home/user/tests/TopLevel.xml", @@ -118,9 +118,6 @@ working directory*. "SetupScript" : "echo 'bouh'", - "TestPlatformHost" : "localhost 5001", - "ParameterFramworkHost" : "localhost 5000", - "CoverageDir" : "/home/user/parameter-framework/tools/coverage", "CoverageFile" : "coverage.html", "PfwDomainConfFile" : "MyConfigurableDomains.xml" diff --git a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py index 46096fd5e..49b73b248 100644 --- a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py +++ b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py @@ -59,17 +59,20 @@ def __init__(self, # Prepare basic commands halCommand = ["remote-process", - configParser["TestPlatformHost"]] + "localhost", + configParser["TestPlatformPort"]] setCriteriaCommand = halCommand + ["setCriterionState"] testPlatformHostCommand = ["remote-process", - configParser["TestPlatformHost"]] + "localhost", + configParser["TestPlatformPort"]] self.__logFileName = configParser["LogFile"] # Commands self.__startTestPlatformCmd = [configParser["PrefixCommand"], "test-platform", - configParser["PfwConfFile"]] + configParser["PfwConfFile"], + configParser["TestPlatformPort"]] self.__createCriterionCmd = [configParser["PrefixCommand"]] self.__createCriterionCmd.extend(testPlatformHostCommand) From 9e7ba5f8b5d3ea20311439b09d8d59d4a265f04a Mon Sep 17 00:00:00 2001 From: David Wagner Date: Thu, 9 Jul 2015 10:42:38 +0200 Subject: [PATCH 10/15] Client Simulator: remove the SetupScript config item Since this script is only run once in the entire lifetime of a Client Simulator instance ( before running the scenarios), it can simply be manually executed by the user. Signed-off-by: David Wagner --- tools/clientSimulator/README.md | 3 --- .../clientsimulator/configuration/ConfigParser.py | 1 - .../clientsimulator/testGenerator/TestLauncher.py | 7 ------- 3 files changed, 11 deletions(-) diff --git a/tools/clientSimulator/README.md b/tools/clientSimulator/README.md index c1da90ec6..b382fcded 100644 --- a/tools/clientSimulator/README.md +++ b/tools/clientSimulator/README.md @@ -84,7 +84,6 @@ A test directory should contains a `conf.json` file containing: - The path to the actions definitions (aka "ActionGatherer") file (optional) (see below). - The absolute path to the log output file (optional). -- A setup script (inline shell) (optional). - The absolute path to the directory containing the coverage generation tool (optional; for coverage only). - The path to the html coverage output file (optional; for coverage only). @@ -116,8 +115,6 @@ working directory*. "LogFile" : "tests.log", - "SetupScript" : "echo 'bouh'", - "CoverageDir" : "/home/user/parameter-framework/tools/coverage", "CoverageFile" : "coverage.html", "PfwDomainConfFile" : "MyConfigurableDomains.xml" diff --git a/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py b/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py index 6cc6b6420..53d8932d5 100644 --- a/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py +++ b/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py @@ -44,7 +44,6 @@ def __init__(self, confFileName, testsDirectory, consoleLogger): # Preparing files and directory paths for key in ["CriterionFile", "ScriptsFile", - "SetupScript", "ActionGathererFile", "ScenariosDirectory", "LogFile", diff --git a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py index 49b73b248..e7d0209d4 100644 --- a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py +++ b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py @@ -88,8 +88,6 @@ def __init__(self, self.__applyConfigurationsCmd.extend(halCommand) self.__applyConfigurationsCmd.append("applyConfigurations") - self.__setupScript = [configParser["SetupScript"]] - # Command used to generate coverage self.__coverageCmd = [ "eval", @@ -121,11 +119,6 @@ def scripts(self): def init(self, criterionClasses, isVerbose): """ Initialise the Pseudo HAL """ - # Use user script to setup environment as requested before to do - # anything - self.__logger.info("Launching Setup script") - self.__call_process(self.__setupScript) - self.__logger.info("Pseudo Hal Initialisation") # Test platform is launched asynchronously and not as script self.__call_process(self.__startTestPlatformCmd, True) From 25da97a5d3fa60a1cd3fadb2525ef4abc8cdc3c7 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Thu, 9 Jul 2015 10:50:23 +0200 Subject: [PATCH 11/15] Client Simulator/configuration: make all paths relative to the test dir Now, all paths in conf.json must be absolute or relative to the test directory. This harmonizes all the paths and avoids describing special cases. Also, there was a mistake in documentation regarding how the "LogFile" path is evaluated. Signed-off-by: David Wagner --- tools/clientSimulator/README.md | 13 +++++-------- .../clientsimulator/configuration/ConfigParser.py | 1 + 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tools/clientSimulator/README.md b/tools/clientSimulator/README.md index b382fcded..9f5a27712 100644 --- a/tools/clientSimulator/README.md +++ b/tools/clientSimulator/README.md @@ -78,13 +78,13 @@ A test directory should contains a `conf.json` file containing: - The port on which the test-platform should be started. - The criterion file path (see [this README](https://github.com/01org/parameter-framework/tree/master/tools/xmlGenerator#domaingeneratorpy)). -- The absolute path to the Parameter Framework toplevel configuration file. +- The path to the Parameter Framework toplevel configuration file. - The path to the directory containing the scenario files. - The path to the scripts definitions file (optional) (see below). - The path to the actions definitions (aka "ActionGatherer") file (optional) (see below). -- The absolute path to the log output file (optional). -- The absolute path to the directory containing the coverage generation tool +- The path to the log output file (optional). +- The path to the directory containing the coverage generation tool (optional; for coverage only). - The path to the html coverage output file (optional; for coverage only). - The path to the Parameter Framework domain configuration file (optional; for @@ -93,11 +93,8 @@ A test directory should contains a `conf.json` file containing: All these *must* be defined in `conf.json`; when marked "optional", it means that they *may be empty but still need to be defined*. -Unless otherwise noted, paths in `conf.json` should be relative and will be -evaluated *relative to the test directory*. When we mention absolute path, you -may still fill a relative path but it will be evaluated *relative to the -working directory*. - +Relative paths in `conf.json` will be evaluated *relative to the test +directory*. ## Example Client Simulator configuration file diff --git a/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py b/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py index 53d8932d5..af557d46f 100644 --- a/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py +++ b/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py @@ -43,6 +43,7 @@ def __init__(self, confFileName, testsDirectory, consoleLogger): # Preparing files and directory paths for key in ["CriterionFile", + "PfwConfFile" "ScriptsFile", "ActionGathererFile", "ScenariosDirectory", From ef20c0cdf437fc44c249cb03a18441d17d21cf40 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Thu, 9 Jul 2015 11:52:40 +0200 Subject: [PATCH 12/15] Client Simulator/conf.json: Make several config items optional ScriptsFile, ActionGathererFile, LogFile, CoverageFile, CoverageDir and PfwDomainConfFile are now optional and can be left out of conf.json. Signed-off-by: David Wagner --- tools/clientSimulator/README.md | 9 +++------ .../configuration/ConfigParser.py | 16 +++++++++++----- .../clientsimulator/scenario/Scenario.py | 6 ++++-- .../testGenerator/TestLauncher.py | 6 ++++-- tools/clientSimulator/pfClientSimulator.py | 16 +++++++++++----- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/tools/clientSimulator/README.md b/tools/clientSimulator/README.md index 9f5a27712..8898c33c7 100644 --- a/tools/clientSimulator/README.md +++ b/tools/clientSimulator/README.md @@ -73,8 +73,8 @@ to learn what it is about. A test directory should contains a `conf.json` file containing: -- The desired command prefix (optional; e.g. `adb shell` in order to execute - tests on an android board or empty to execute locally). +- The desired command prefix (e.g. `adb shell` in order to execute tests on an + android board or empty to execute locally). - The port on which the test-platform should be started. - The criterion file path (see [this README](https://github.com/01org/parameter-framework/tree/master/tools/xmlGenerator#domaingeneratorpy)). @@ -83,16 +83,13 @@ A test directory should contains a `conf.json` file containing: - The path to the scripts definitions file (optional) (see below). - The path to the actions definitions (aka "ActionGatherer") file (optional) (see below). -- The path to the log output file (optional). +- The path to the log output file (optional but needed for coverage). - The path to the directory containing the coverage generation tool (optional; for coverage only). - The path to the html coverage output file (optional; for coverage only). - The path to the Parameter Framework domain configuration file (optional; for coverage only). -All these *must* be defined in `conf.json`; when marked "optional", it means -that they *may be empty but still need to be defined*. - Relative paths in `conf.json` will be evaluated *relative to the test directory*. diff --git a/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py b/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py index af557d46f..8a513ffde 100644 --- a/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py +++ b/tools/clientSimulator/clientsimulator/configuration/ConfigParser.py @@ -41,17 +41,23 @@ def __init__(self, confFileName, testsDirectory, consoleLogger): with open(confFileName, "r") as testFile: self.__conf = json.load(testFile) - # Preparing files and directory paths + # Preparing mandatory files and directory paths for key in ["CriterionFile", - "PfwConfFile" - "ScriptsFile", + "PfwConfFile", + "ScenariosDirectory"]: + self.__conf[key] = os.path.join(testsDirectory, self.__conf[key]) + + # Preparing optional files and directory paths + for key in ["ScriptsFile", "ActionGathererFile", - "ScenariosDirectory", "LogFile", "CoverageFile", "CoverageDir", "PfwDomainConfFile"]: - self.__conf[key] = os.path.join(testsDirectory, self.__conf[key]) + try: + self.__conf[key] = os.path.join(testsDirectory, self.__conf[key]) + except KeyError as e: + self.__conf[key] = "" self.__logger = logging.getLogger(__name__) self.__logger.addHandler(consoleLogger) diff --git a/tools/clientSimulator/clientsimulator/scenario/Scenario.py b/tools/clientSimulator/clientsimulator/scenario/Scenario.py index a14a2483e..965fbe948 100644 --- a/tools/clientSimulator/clientsimulator/scenario/Scenario.py +++ b/tools/clientSimulator/clientsimulator/scenario/Scenario.py @@ -99,8 +99,10 @@ def __parseScenarioActions(self, scenarioFileName, actionGathererFileName): # Parsing the action Gatherer file which allows defining new # actions types - with open(actionGathererFileName, "r") as actionGathererFile: - scenarioGatheredActions = json.load(actionGathererFile) + scenarioGatheredActions = {} + if actionGathererFileName: + with open(actionGathererFileName, "r") as actionGathererFile: + scenarioGatheredActions = json.load(actionGathererFile) for action in scenarioActions: actionDefinedType = self.__getActionType(action) diff --git a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py index e7d0209d4..eb68bfb74 100644 --- a/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py +++ b/tools/clientSimulator/clientsimulator/testGenerator/TestLauncher.py @@ -103,8 +103,10 @@ def __init__(self, # Prepare script Commands # Loading possible scripts - with open(configParser["ScriptsFile"], 'r') as scriptFile: - self.__rawScripts = json.load(scriptFile) + self.__rawScripts = {} + if configParser["ScriptsFile"]: + with open(configParser["ScriptsFile"], 'r') as scriptFile: + self.__rawScripts = json.load(scriptFile) self.__availableLaunchType = ["asynchronous", "synchronous"] diff --git a/tools/clientSimulator/pfClientSimulator.py b/tools/clientSimulator/pfClientSimulator.py index 82cbedef6..490349cd3 100755 --- a/tools/clientSimulator/pfClientSimulator.py +++ b/tools/clientSimulator/pfClientSimulator.py @@ -134,12 +134,18 @@ def main(): args.test_directory)) exit(1) - configParser = ConfigParser( - os.path.join( + try: + configParser = ConfigParser( + os.path.join( + args.test_directory, + "conf.json"), args.test_directory, - "conf.json"), - args.test_directory, - consoleLogger) + consoleLogger) + except KeyError as e: + logger.error( + "Missing mandatory configuration item {} in the" + " conf.json file".format(e)) + exit(1) # Always write all log in the file logging.basicConfig(level=logging.DEBUG, From a932cd2bb80657fc7d433fde269f373dbedd6ec7 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Thu, 9 Jul 2015 13:56:05 +0200 Subject: [PATCH 13/15] Client Simulator: use lists instead of dicts for menu handling Dicts had no added value over lists since menus are a) sorted and b) indexed by a number. Using lists instead simplifies the code a bit. Signed-off-by: David Wagner --- .../userInteraction/UserInteractor.py | 78 +++++++++---------- tools/clientSimulator/pfClientSimulator.py | 8 +- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/tools/clientSimulator/clientsimulator/userInteraction/UserInteractor.py b/tools/clientSimulator/clientsimulator/userInteraction/UserInteractor.py index d481b61c9..23b065629 100644 --- a/tools/clientSimulator/clientsimulator/userInteraction/UserInteractor.py +++ b/tools/clientSimulator/clientsimulator/userInteraction/UserInteractor.py @@ -55,9 +55,9 @@ def getMenu(cls, options, cancelSentence="Go Back"): """ Dynamic Menu Generator : - :param options: dictionnary containing, the invite string - and the function to launch - :type options: dict + :param options: list containing tuples of a) the invite string and + b) the function to launch + :type options: list :param cancelSentence: title of the menu entry that will be appended after the provided options, in order to exit the menu. For top-level menus, it is advised to pass "Quit" as argument. @@ -66,7 +66,7 @@ def getMenu(cls, options, cancelSentence="Go Back"): while True: print("\nPlease Make a choice : ") - for numMenu, (sentenceMenu, fonc) in sorted(options.items()): + for numMenu, (sentenceMenu, fonc) in enumerate(options): print("\t{}. {}".format(numMenu, sentenceMenu)) # Lastly, append an option to go to the previous menu/quit @@ -78,6 +78,9 @@ def getMenu(cls, options, cancelSentence="Go Back"): if choice == len(options): # The user has selected the "cancel" option break + if choice < 0: + # Negative values make no sense + raise KeyError(choice) options[choice][1]() except (KeyError, ValueError) as e: @@ -88,11 +91,11 @@ def launchInteractiveMode(self): Interactive Mode : Set up a menu which allow users to personnalize a Test and to Launch it """ - optionsMenu = { - 0: ("Edit Vector", self.__editVector), - 1: ("Apply Configuration", self.__applyConfiguration), - 2: ("Launch Script", self.__launchScript) - } + optionsMenu = [ + ("Edit Vector", self.__editVector), + ("Apply Configuration", self.__applyConfiguration), + ("Launch Script", self.__launchScript) + ] UserInteractor.getMenu(optionsMenu, "Quit") @@ -112,11 +115,11 @@ def __launchScript(self): script to run. """ - optionScript = { - num: ("{} scripts".format(script), + optionScript = [ + ("{} scripts".format(script), DynamicCallHelper(self.__testLauncher.executeScript, script)) - for num, script in enumerate(self.__testLauncher.scripts) - } + for script in self.__testLauncher.scripts + ] UserInteractor.getMenu(optionScript) @@ -136,37 +139,34 @@ def __editCriterion(self, criterion): :type criterion: Criterion """ - optionEditCriterion = {} + optionEditCriterion = [] for possibleValue in [x for x in criterion.allowedValues() if not x in criterion.currentValue and not x == criterion.noValue]: - optionEditCriterion[ - len(optionEditCriterion)] = ( - "Set {}".format(possibleValue), - DynamicCallHelper( - self.__setCriterion, - criterion, - possibleValue)) + optionEditCriterion.append( + ("Set {}".format(possibleValue), + DynamicCallHelper( + self.__setCriterion, + criterion, + possibleValue))) if InclusiveCriterion in criterion.__class__.__bases__: # Inclusive criterion : display unset value (default when empty) for possibleValue in criterion.currentValue: - optionEditCriterion[ - len(optionEditCriterion)] = ( - "Unset {}".format(possibleValue), - DynamicCallHelper( - self.__removeCriterionValue, - criterion, - possibleValue)) + optionEditCriterion.append( + ("Unset {}".format(possibleValue), + DynamicCallHelper( + self.__removeCriterionValue, + criterion, + possibleValue))) else: # Exclusive criterion : display default value - optionEditCriterion[ - len(optionEditCriterion)] = ( - "Set Default", - DynamicCallHelper( - self.__setCriterion, - criterion, - criterion.noValue)) + optionEditCriterion.append( + ("Set Default", + DynamicCallHelper( + self.__setCriterion, + criterion, + criterion.noValue))) UserInteractor.getMenu(optionEditCriterion) @@ -177,11 +177,11 @@ def __editVector(self): Allow to change the value of several criterions through a menu. """ - optionEdit = { - num: ("Edit {}".format(cri.__class__.__name__), + optionEdit = [ + ("Edit {}".format(cri.__class__.__name__), DynamicCallHelper(self.__editCriterion, cri)) - for num, cri in enumerate(self.__criterions) - } + for cri in self.__criterions + ] UserInteractor.getMenu(optionEdit) diff --git a/tools/clientSimulator/pfClientSimulator.py b/tools/clientSimulator/pfClientSimulator.py index 490349cd3..f315f58d5 100755 --- a/tools/clientSimulator/pfClientSimulator.py +++ b/tools/clientSimulator/pfClientSimulator.py @@ -179,8 +179,7 @@ def main(): testLauncher, testFactory.generateTestVector()).launchInteractiveMode() else: - scenarioOptions = { - scenarioNumber: + scenarioOptions = [ (scenarioFileName, DynamicCallHelper( launchScenario, @@ -192,9 +191,8 @@ def main(): testFactory, testLauncher )) - for scenarioNumber, scenarioFileName in enumerate( - sorted(os.listdir(configParser["ScenariosDirectory"]))) - } + for scenarioFileName in sorted(os.listdir(configParser["ScenariosDirectory"])) + ] if args.scenario is not None: scenarioOptions[args.scenario][1]() # Let the user choose more scenarios after the one chosen by command line From 6a11f0be8ac6d527848aded8ca6396286dc2d057 Mon Sep 17 00:00:00 2001 From: David Wagner Date: Thu, 9 Jul 2015 14:02:20 +0200 Subject: [PATCH 14/15] Client Simulator: make the 'script' menu look like other menus Each menu entry used to say "