summaryrefslogtreecommitdiffstats
path: root/test/test-fixed-point-parameter/Main.py
blob: ed5787399f04ac2137a683e5edad48a73471d1fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#!/usr/bin/python2.7
#
# 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.

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, size, integral, fractional):
        self._pfwClient = pfwClient
        self._paramPath = '/Test/test/%d/q%d.%d' % (size, 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
        """

        runSuccess = True

        for value in self._shouldWork:
            value = value.normalize()

            print('Testing %s for %s' % (value, self._paramPath))
            value, success = self.checkBounds(value)
            if not success:
                runSuccess = False
                print('Bound ERROR for %s' % self._paramPath)
                continue

            value, success = self.checkSanity(value)
            if not success:
                runSuccess = False
                print('Sanity ERROR %s' % self._paramPath)
                continue

            value, success = self.checkConsistency(value)
            if not success:
                runSuccess = False
                print('Consistency ERROR %s' % self._paramPath)
                continue

            value, success = self.checkBijectivity(value)
            if not success:
                runSuccess = False
                print('Bijectivity ERROR %s' % self._paramPath)
                continue

        for value in self._shouldBreak:
            value = value.normalize()
            print('Testing invalid value %s for %s' % (value, self._paramPath))
            value, success = self.checkBounds(value)
            if success:
                runSuccess = False
                print("ERROR: This test should have failed but it has not")

        return runSuccess

    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'
        self._configPath = configPath

    def __enter__(self):
        # launch test platform in deamon mode
        subprocess.call(['test-platform', '-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'

    success = True

    with PfwClient(configPath) as pfw:
        for size in [8, 16, 32]:
            for integral in range(0,  size):
                for fractional in range (0,  size - integral):
                    tester = FixedPointTester(pfw, size, integral, fractional)
                    success = tester.run() and success

    exit(0 if success else 1)