From fcce1cc05609bb70be328c57d5a39dbf422b5eec Mon Sep 17 00:00:00 2001 From: Mattijs Korpershoek Date: Thu, 22 May 2014 16:56:32 +0200 Subject: Fixed point test script BZ: 197723 This python script performs several checks on fixed point parameters: - Bound check: Can we set a value? - Sanity check: If we get this previously set parameter, is the value approximately the same? There should be at maximum a quantum difference between the original value and the returned value. - Consistency check: Can we set the value we got from the sanity check? - Bijectivity check: If we get the parameter we set a second time, is the result the same as the value we got from the set we did in the Sanity check? Change-Id: I7b4d61ec740139b0ee70a44b6b38009507a569ee Signed-off-by: Mattijs Korpershoek --- test/test-fixed-point-parameter/Main.py | 240 ++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100755 test/test-fixed-point-parameter/Main.py (limited to 'test') diff --git a/test/test-fixed-point-parameter/Main.py b/test/test-fixed-point-parameter/Main.py new file mode 100755 index 0000000..09cff28 --- /dev/null +++ b/test/test-fixed-point-parameter/Main.py @@ -0,0 +1,240 @@ +#!/usr/bin/python2.7 +# +# Copyright (c) 2014, 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. + +import sys +import subprocess +from decimal import Decimal + +class FixedPointTester(): + """ Made for testing a particular Qn.m number + + As a convention, we use: + * n is the fractional part + * m is the integral part + + This class computes several specific numbers for a given Qn.m number. + + For each of those numbers, we run 4 checks: + * Bound check + * Sanity check + * Consistency check + * Bijectivity check + Which are documented below. + """ + def __init__(self, pfwClient, integral, fractional): + self._pfwClient = pfwClient + self._paramPath = '/Test/test/q%d.%d' % (integral, fractional) + + # quantum is the step we have between two numbers + # encoded in Qn.m format + self._quantum = 2 ** -fractional + + # The maximum value we can encode for a given Qn.m. + # Since we also need to encode the 0, we have one quantum missing on + # the positive maximum + self._upperAllowedBound = (2 ** integral) - self._quantum + + # The minimum value that we can encode for a given Qn.m. + # This one does not need a quantum substraction since we already did + # that on the maximum + self._lowerAllowedBound = -(2 ** integral) + + self._shouldWork = [ + Decimal(0), + Decimal(self._lowerAllowedBound), + Decimal(self._upperAllowedBound) + ] + + # bigValue is to be sure a value far out of range is refused + bigValue = (2 * self._quantum) + # little is to be sure a value just out of range is refused + littleValue = 10 ** -fractional + self._shouldBreak = [ + Decimal(self._lowerAllowedBound) - Decimal(bigValue), + Decimal(self._upperAllowedBound) + Decimal(bigValue), + Decimal(self._lowerAllowedBound) - Decimal(littleValue), + Decimal(self._upperAllowedBound) + Decimal(littleValue) + ] + + + def run(self): + """ Runs the test suite for a given Qn.m number + """ + for value in self._shouldWork: + print('Testing %s for %s' % (value, self._paramPath)) + value, success = self.checkBounds(value) + if not success: + print('Bound ERROR for %s' % self._paramPath) + continue + + value, success = self.checkSanity(value) + if not success: + print('Sanity ERROR %s' % self._paramPath) + continue + + value, success = self.checkConsistency(value) + if not success: + print('Consistency ERROR %s' % self._paramPath) + continue + + value, success = self.checkBijectivity(value) + if not success: + print('Bijectivity ERROR %s' % self._paramPath) + continue + + for value in self._shouldBreak: + print('Testing invalid value %s for %s' % (value, self._paramPath)) + value, success = self.checkBounds(value) + if success: + print("ERROR: This test should have failed but it has not") + + def checkBounds(self, valueToSet): + """ Checks if we are able to set valueToSet via the parameter-framework + + valueToSet -- the value we are trying to set + + returns: the value we are trying to set + returns: True if we are able to set, False otherwise + """ + returnCode = self._pfwClient.set(self._paramPath, str(valueToSet)) + if returnCode != 0: + return (valueToSet, False) + + return (valueToSet, True) + + + def checkSanity(self, valuePreviouslySet): + """ Checks if the value we get is still approximately the same + as we attempted to set. The value can have a slight round error which + is tolerated. + + valuePreviouslySet -- the value we had previously set + + returns: the value the parameter-framework returns us after the get + returns: True if we are able to set, False otherwise + """ + firstGet = self._pfwClient.get(self._paramPath) + + try: + returnValue = Decimal(firstGet) + except ValueError: + print("ERROR: Can't convert %s to a decimal" % firstGet) + return firstGet, False + + upperAllowedValue = Decimal(valuePreviouslySet) + (Decimal(self._quantum) / Decimal(2)) + lowerAllowedValue = Decimal(valuePreviouslySet) - (Decimal(self._quantum) / Decimal(2)) + + if not (lowerAllowedValue <= returnValue <= upperAllowedValue): + print('%s <= %s <= %s is not true' % + (lowerAllowedValue, returnValue, upperAllowedValue)) + return firstGet, False + + return firstGet, True + + def checkConsistency(self, valuePreviouslyGotten): + """ Checks if we are able to set the value that the parameter framework + just returned to us. + + valuePreviouslyGotten -- the value we are trying to set + + valueToSet -- the value we are trying to set + returns: True if we are able to set, False otherwise + """ + returnCode = pfw.set(self._paramPath, valuePreviouslyGotten) + if returnCode != 0: + return valuePreviouslyGotten, False + + return valuePreviouslyGotten, True + + def checkBijectivity(self, valuePreviouslySet): + """ Checks that the second get value is strictly equivalent to the + consistency set. This ensures that the parameter-framework behaves as + expected. + + valuePreviouslySet -- the value we had previously set + + returns: value the parameter-framework returns us after the second get + returns: True if we are able to set, False otherwise + """ + secondGet = pfw.get(self._paramPath) + if secondGet != valuePreviouslySet: + return secondGet, False + + return secondGet, True + +class PfwClient(): + + def __init__(self, configPath): + self._address = 'localhost' + self._port = '5066' + self._testPlatformPort = '5063' + self._pathToExec = 'remote-process_host' + self._configPath = configPath + + def __enter__(self): + # launch test platform in deamon mode + subprocess.call(['test-platform_host', '-d', self._configPath, self._testPlatformPort]) + subprocess.call([self._pathToExec, self._address, self._testPlatformPort, 'start']) + self._callCommand(['setTuningMode', 'on']) + return self + + def __exit__(self, type, value, traceback): + subprocess.call([self._pathToExec, self._address, self._testPlatformPort, 'exit']) + + def _callCommand(self, commandList): + shellCommand = [self._pathToExec, self._address, self._port] + shellCommand.extend(commandList) + # pipes are used to redirect the command output to a variable + subProc = subprocess.Popen(shellCommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + commandOutPut, _ = subProc.communicate() + returnCode = subProc.returncode + return commandOutPut, returnCode + + def set(self, parameter, value): + print('set %s <--- %s' % (parameter, value)) + (returnValue, returnCode) = self._callCommand(['setParameter', parameter, value]) + return returnCode + + def get(self, parameter): + (returnValue, _) = self._callCommand(['getParameter', parameter]) + print('get %s ---> %s' % (parameter, returnValue.strip())) + return returnValue.strip() + +if __name__ == '__main__': + # It is necessary to add a ./ in front of the path, otherwise the parameter-framework + # does not recognize the string as a path. + configPath = './ParameterFrameworkConfiguration.xml' + + with PfwClient(configPath) as pfw: + for integral in range(0, 31): + for fractional in range (0, 31 - integral): + tester = FixedPointTester(pfw, integral, fractional) + tester.run() + -- cgit v1.1