1cc1dc7a3Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0
2cc1dc7a3Sopenharmony_ci# -----------------------------------------------------------------------------
3cc1dc7a3Sopenharmony_ci# Copyright 2020-2022 Arm Limited
4cc1dc7a3Sopenharmony_ci#
5cc1dc7a3Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6cc1dc7a3Sopenharmony_ci# use this file except in compliance with the License. You may obtain a copy
7cc1dc7a3Sopenharmony_ci# of the License at:
8cc1dc7a3Sopenharmony_ci#
9cc1dc7a3Sopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
10cc1dc7a3Sopenharmony_ci#
11cc1dc7a3Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
12cc1dc7a3Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13cc1dc7a3Sopenharmony_ci# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14cc1dc7a3Sopenharmony_ci# License for the specific language governing permissions and limitations
15cc1dc7a3Sopenharmony_ci# under the License.
16cc1dc7a3Sopenharmony_ci# -----------------------------------------------------------------------------
17cc1dc7a3Sopenharmony_ci"""
18cc1dc7a3Sopenharmony_ciA ResultSet stores a set of results about the performance of a TestSet. Each
19cc1dc7a3Sopenharmony_ciset keeps result Records for each image and block size tested, that store the
20cc1dc7a3Sopenharmony_ciPSNR and coding time.
21cc1dc7a3Sopenharmony_ci
22cc1dc7a3Sopenharmony_ciResultSets are often backed by a CSV file on disk, and a ResultSet can be
23cc1dc7a3Sopenharmony_cicompared against a set of reference results created by an earlier test run.
24cc1dc7a3Sopenharmony_ci"""
25cc1dc7a3Sopenharmony_ci
26cc1dc7a3Sopenharmony_ci
27cc1dc7a3Sopenharmony_ciimport csv
28cc1dc7a3Sopenharmony_ciimport enum
29cc1dc7a3Sopenharmony_ciimport numpy as np
30cc1dc7a3Sopenharmony_ciimport os
31cc1dc7a3Sopenharmony_ci
32cc1dc7a3Sopenharmony_ci
33cc1dc7a3Sopenharmony_ci@enum.unique
34cc1dc7a3Sopenharmony_ciclass Result(enum.IntEnum):
35cc1dc7a3Sopenharmony_ci    """
36cc1dc7a3Sopenharmony_ci    An enumeration of test result status values.
37cc1dc7a3Sopenharmony_ci
38cc1dc7a3Sopenharmony_ci    Attributes:
39cc1dc7a3Sopenharmony_ci        NOTRUN: The test has not been run.
40cc1dc7a3Sopenharmony_ci        PASS: The test passed.
41cc1dc7a3Sopenharmony_ci        WARN: The test image quality was below the pass threshold but above
42cc1dc7a3Sopenharmony_ci            the fail threshold.
43cc1dc7a3Sopenharmony_ci        FAIL: The test image quality was below the fail threshold.
44cc1dc7a3Sopenharmony_ci    """
45cc1dc7a3Sopenharmony_ci    NOTRUN = 0
46cc1dc7a3Sopenharmony_ci    PASS = 1
47cc1dc7a3Sopenharmony_ci    WARN = 2
48cc1dc7a3Sopenharmony_ci    FAIL = 3
49cc1dc7a3Sopenharmony_ci
50cc1dc7a3Sopenharmony_ci
51cc1dc7a3Sopenharmony_ciclass ResultSummary():
52cc1dc7a3Sopenharmony_ci    """
53cc1dc7a3Sopenharmony_ci    An result summary data container, storing number of results of each type.
54cc1dc7a3Sopenharmony_ci
55cc1dc7a3Sopenharmony_ci    Attributes:
56cc1dc7a3Sopenharmony_ci        notruns: The number of tests that did not run.
57cc1dc7a3Sopenharmony_ci        passes: The number of tests that passed.
58cc1dc7a3Sopenharmony_ci        warnings: The number of tests that produced a warning.
59cc1dc7a3Sopenharmony_ci        fails: The number of tests that failed.
60cc1dc7a3Sopenharmony_ci        tTimes: Total time speedup vs reference (<1 is slower, >1 is faster).
61cc1dc7a3Sopenharmony_ci        cTimes: Coding time speedup vs reference (<1 is slower, >1 is faster).
62cc1dc7a3Sopenharmony_ci        psnrs: Coding time quality vs reference (<0 is worse, >0 is better).
63cc1dc7a3Sopenharmony_ci    """
64cc1dc7a3Sopenharmony_ci
65cc1dc7a3Sopenharmony_ci    def __init__(self):
66cc1dc7a3Sopenharmony_ci        """
67cc1dc7a3Sopenharmony_ci        Create a new result summary.
68cc1dc7a3Sopenharmony_ci        """
69cc1dc7a3Sopenharmony_ci        # Pass fail metrics
70cc1dc7a3Sopenharmony_ci        self.notruns = 0
71cc1dc7a3Sopenharmony_ci        self.passes = 0
72cc1dc7a3Sopenharmony_ci        self.warnings = 0
73cc1dc7a3Sopenharmony_ci        self.fails = 0
74cc1dc7a3Sopenharmony_ci
75cc1dc7a3Sopenharmony_ci        # Relative results
76cc1dc7a3Sopenharmony_ci        self.tTimesRel = []
77cc1dc7a3Sopenharmony_ci        self.cTimesRel = []
78cc1dc7a3Sopenharmony_ci        self.psnrRel = []
79cc1dc7a3Sopenharmony_ci
80cc1dc7a3Sopenharmony_ci        # Absolute results
81cc1dc7a3Sopenharmony_ci        self.cTime = []
82cc1dc7a3Sopenharmony_ci        self.psnr = []
83cc1dc7a3Sopenharmony_ci
84cc1dc7a3Sopenharmony_ci    def add_record(self, record):
85cc1dc7a3Sopenharmony_ci        """
86cc1dc7a3Sopenharmony_ci        Add a record to this summary.
87cc1dc7a3Sopenharmony_ci
88cc1dc7a3Sopenharmony_ci        Args:
89cc1dc7a3Sopenharmony_ci            record (Record): The Record to add.
90cc1dc7a3Sopenharmony_ci        """
91cc1dc7a3Sopenharmony_ci        if record.status == Result.PASS:
92cc1dc7a3Sopenharmony_ci            self.passes += 1
93cc1dc7a3Sopenharmony_ci        elif record.status == Result.WARN:
94cc1dc7a3Sopenharmony_ci            self.warnings += 1
95cc1dc7a3Sopenharmony_ci        elif record.status == Result.FAIL:
96cc1dc7a3Sopenharmony_ci            self.fails += 1
97cc1dc7a3Sopenharmony_ci        else:
98cc1dc7a3Sopenharmony_ci            self.notruns += 1
99cc1dc7a3Sopenharmony_ci
100cc1dc7a3Sopenharmony_ci        if record.tTimeRel is not None:
101cc1dc7a3Sopenharmony_ci            self.tTimesRel.append(record.tTimeRel)
102cc1dc7a3Sopenharmony_ci            self.cTimesRel.append(record.cTimeRel)
103cc1dc7a3Sopenharmony_ci            self.psnrRel.append(record.psnrRel)
104cc1dc7a3Sopenharmony_ci
105cc1dc7a3Sopenharmony_ci            self.cTime.append(record.cTime)
106cc1dc7a3Sopenharmony_ci            self.psnr.append(record.psnr)
107cc1dc7a3Sopenharmony_ci
108cc1dc7a3Sopenharmony_ci    def get_worst_result(self):
109cc1dc7a3Sopenharmony_ci        """
110cc1dc7a3Sopenharmony_ci        Get the worst result in this set.
111cc1dc7a3Sopenharmony_ci
112cc1dc7a3Sopenharmony_ci        Returns:
113cc1dc7a3Sopenharmony_ci            Result: The worst test result.
114cc1dc7a3Sopenharmony_ci        """
115cc1dc7a3Sopenharmony_ci        if self.fails:
116cc1dc7a3Sopenharmony_ci            return Result.FAIL
117cc1dc7a3Sopenharmony_ci
118cc1dc7a3Sopenharmony_ci        if self.warnings:
119cc1dc7a3Sopenharmony_ci            return Result.WARN
120cc1dc7a3Sopenharmony_ci
121cc1dc7a3Sopenharmony_ci        if self.passes:
122cc1dc7a3Sopenharmony_ci            return Result.PASS
123cc1dc7a3Sopenharmony_ci
124cc1dc7a3Sopenharmony_ci        return Result.NOTRUN
125cc1dc7a3Sopenharmony_ci
126cc1dc7a3Sopenharmony_ci    def __str__(self):
127cc1dc7a3Sopenharmony_ci        # Overall pass/fail results
128cc1dc7a3Sopenharmony_ci        overall = self.get_worst_result().name
129cc1dc7a3Sopenharmony_ci        dat = (overall, self.passes, self.warnings, self.fails)
130cc1dc7a3Sopenharmony_ci        result = ["\nSet Status: %s (Pass: %u | Warn: %u | Fail: %u)" % dat]
131cc1dc7a3Sopenharmony_ci
132cc1dc7a3Sopenharmony_ci        if (self.tTimesRel):
133cc1dc7a3Sopenharmony_ci            # Performance summaries
134cc1dc7a3Sopenharmony_ci            dat = (np.mean(self.tTimesRel), np.std(self.tTimesRel))
135cc1dc7a3Sopenharmony_ci            result.append("\nTotal speed:   Mean:  %+0.3f x   Std: %0.2f x" % dat)
136cc1dc7a3Sopenharmony_ci
137cc1dc7a3Sopenharmony_ci            dat = (np.mean(self.cTimesRel), np.std(self.cTimesRel))
138cc1dc7a3Sopenharmony_ci            result.append("Coding speed:  Mean:  %+0.3f x   Std: %0.2f x" % dat)
139cc1dc7a3Sopenharmony_ci
140cc1dc7a3Sopenharmony_ci            dat = (np.mean(self.psnrRel), np.std(self.psnrRel))
141cc1dc7a3Sopenharmony_ci            result.append("Quality diff:  Mean:  %+0.3f dB  Std: %0.2f dB" % dat)
142cc1dc7a3Sopenharmony_ci
143cc1dc7a3Sopenharmony_ci            dat = (np.mean(self.cTime), np.std(self.cTime))
144cc1dc7a3Sopenharmony_ci            result.append("Coding time:   Mean:  %+0.3f s   Std: %0.2f s" % dat)
145cc1dc7a3Sopenharmony_ci
146cc1dc7a3Sopenharmony_ci            dat = (np.mean(self.psnr), np.std(self.psnr))
147cc1dc7a3Sopenharmony_ci            result.append("Quality:       Mean: %+0.3f dB  Std: %0.2f dB" % dat)
148cc1dc7a3Sopenharmony_ci
149cc1dc7a3Sopenharmony_ci        return "\n".join(result)
150cc1dc7a3Sopenharmony_ci
151cc1dc7a3Sopenharmony_ci
152cc1dc7a3Sopenharmony_ciclass Record():
153cc1dc7a3Sopenharmony_ci    """
154cc1dc7a3Sopenharmony_ci    A single result record, sotring results for a singel image and block size.
155cc1dc7a3Sopenharmony_ci
156cc1dc7a3Sopenharmony_ci    Attributes:
157cc1dc7a3Sopenharmony_ci        blkSz: The block size.
158cc1dc7a3Sopenharmony_ci        name: The test image name.
159cc1dc7a3Sopenharmony_ci        psnr: The image quality (PSNR dB)
160cc1dc7a3Sopenharmony_ci        tTime: The total compression time.
161cc1dc7a3Sopenharmony_ci        cTime: The coding compression time.
162cc1dc7a3Sopenharmony_ci        cRate: The coding compression rate.
163cc1dc7a3Sopenharmony_ci        status: The test Result.
164cc1dc7a3Sopenharmony_ci    """
165cc1dc7a3Sopenharmony_ci
166cc1dc7a3Sopenharmony_ci    def __init__(self, blkSz, name, psnr, tTime, cTime, cRate):
167cc1dc7a3Sopenharmony_ci        """
168cc1dc7a3Sopenharmony_ci        Create a result record, initially in the NOTRUN status.
169cc1dc7a3Sopenharmony_ci
170cc1dc7a3Sopenharmony_ci        Args:
171cc1dc7a3Sopenharmony_ci            blkSz (str): The block size.
172cc1dc7a3Sopenharmony_ci            name (str): The test image name.
173cc1dc7a3Sopenharmony_ci            psnr (float): The image quality PSNR, in dB.
174cc1dc7a3Sopenharmony_ci            tTime (float): The total compression time, in seconds.
175cc1dc7a3Sopenharmony_ci            cTime (float): The coding compression time, in seconds.
176cc1dc7a3Sopenharmony_ci            cRate (float): The coding compression rate, in MPix/s.
177cc1dc7a3Sopenharmony_ci            tTimeRel (float): The relative total time speedup vs reference.
178cc1dc7a3Sopenharmony_ci            cTimeRel (float): The relative coding time speedup vs reference.
179cc1dc7a3Sopenharmony_ci            cRateRel (float): The relative rate speedup vs reference.
180cc1dc7a3Sopenharmony_ci            psnrRel (float): The relative PSNR dB vs reference.
181cc1dc7a3Sopenharmony_ci        """
182cc1dc7a3Sopenharmony_ci        self.blkSz = blkSz
183cc1dc7a3Sopenharmony_ci        self.name = name
184cc1dc7a3Sopenharmony_ci        self.psnr = psnr
185cc1dc7a3Sopenharmony_ci        self.tTime = tTime
186cc1dc7a3Sopenharmony_ci        self.cTime = cTime
187cc1dc7a3Sopenharmony_ci        self.cRate = cRate
188cc1dc7a3Sopenharmony_ci        self.status = Result.NOTRUN
189cc1dc7a3Sopenharmony_ci
190cc1dc7a3Sopenharmony_ci        self.tTimeRel = None
191cc1dc7a3Sopenharmony_ci        self.cTimeRel = None
192cc1dc7a3Sopenharmony_ci        self.cRateRel = None
193cc1dc7a3Sopenharmony_ci        self.psnrRel = None
194cc1dc7a3Sopenharmony_ci
195cc1dc7a3Sopenharmony_ci    def set_status(self, result):
196cc1dc7a3Sopenharmony_ci        """
197cc1dc7a3Sopenharmony_ci        Set the result status.
198cc1dc7a3Sopenharmony_ci
199cc1dc7a3Sopenharmony_ci        Args:
200cc1dc7a3Sopenharmony_ci            result (Result): The test result.
201cc1dc7a3Sopenharmony_ci        """
202cc1dc7a3Sopenharmony_ci        self.status = result
203cc1dc7a3Sopenharmony_ci
204cc1dc7a3Sopenharmony_ci    def __str__(self):
205cc1dc7a3Sopenharmony_ci        return "'%s' / '%s'" % (self.blkSz, self.name)
206cc1dc7a3Sopenharmony_ci
207cc1dc7a3Sopenharmony_ci
208cc1dc7a3Sopenharmony_ciclass ResultSet():
209cc1dc7a3Sopenharmony_ci    """
210cc1dc7a3Sopenharmony_ci    A set of results for a TestSet, across one or more block sizes.
211cc1dc7a3Sopenharmony_ci
212cc1dc7a3Sopenharmony_ci    Attributes:
213cc1dc7a3Sopenharmony_ci        testSet: The test set these results are linked to.
214cc1dc7a3Sopenharmony_ci        records: The list of test results.
215cc1dc7a3Sopenharmony_ci    """
216cc1dc7a3Sopenharmony_ci
217cc1dc7a3Sopenharmony_ci    def __init__(self, testSet):
218cc1dc7a3Sopenharmony_ci        """
219cc1dc7a3Sopenharmony_ci        Create a new empty ResultSet.
220cc1dc7a3Sopenharmony_ci
221cc1dc7a3Sopenharmony_ci        Args:
222cc1dc7a3Sopenharmony_ci            testSet (TestSet): The test set these results are linked to.
223cc1dc7a3Sopenharmony_ci        """
224cc1dc7a3Sopenharmony_ci        self.testSet = testSet
225cc1dc7a3Sopenharmony_ci        self.records = []
226cc1dc7a3Sopenharmony_ci
227cc1dc7a3Sopenharmony_ci    def add_record(self, record):
228cc1dc7a3Sopenharmony_ci        """
229cc1dc7a3Sopenharmony_ci        Add a new test record to this result set.
230cc1dc7a3Sopenharmony_ci
231cc1dc7a3Sopenharmony_ci        Args:
232cc1dc7a3Sopenharmony_ci            record (Record): The test record to add.
233cc1dc7a3Sopenharmony_ci        """
234cc1dc7a3Sopenharmony_ci        self.records.append(record)
235cc1dc7a3Sopenharmony_ci
236cc1dc7a3Sopenharmony_ci    def get_record(self, testSet, blkSz, name):
237cc1dc7a3Sopenharmony_ci        """
238cc1dc7a3Sopenharmony_ci        Get a record matching the arguments.
239cc1dc7a3Sopenharmony_ci
240cc1dc7a3Sopenharmony_ci        Args:
241cc1dc7a3Sopenharmony_ci            testSet (TestSet): The test set to get results from.
242cc1dc7a3Sopenharmony_ci            blkSz (str): The block size.
243cc1dc7a3Sopenharmony_ci            name (str): The test name.
244cc1dc7a3Sopenharmony_ci
245cc1dc7a3Sopenharmony_ci        Returns:
246cc1dc7a3Sopenharmony_ci            Record: The test result, if present.
247cc1dc7a3Sopenharmony_ci
248cc1dc7a3Sopenharmony_ci        Raises:
249cc1dc7a3Sopenharmony_ci            KeyError: No match could be found.
250cc1dc7a3Sopenharmony_ci        """
251cc1dc7a3Sopenharmony_ci        if testSet != self.testSet:
252cc1dc7a3Sopenharmony_ci            raise KeyError()
253cc1dc7a3Sopenharmony_ci
254cc1dc7a3Sopenharmony_ci        for record in self.records:
255cc1dc7a3Sopenharmony_ci            if record.blkSz == blkSz and record.name == name:
256cc1dc7a3Sopenharmony_ci                return record
257cc1dc7a3Sopenharmony_ci
258cc1dc7a3Sopenharmony_ci        raise KeyError()
259cc1dc7a3Sopenharmony_ci
260cc1dc7a3Sopenharmony_ci    def get_matching_record(self, other):
261cc1dc7a3Sopenharmony_ci        """
262cc1dc7a3Sopenharmony_ci        Get a record matching the config of another record.
263cc1dc7a3Sopenharmony_ci
264cc1dc7a3Sopenharmony_ci        Args:
265cc1dc7a3Sopenharmony_ci            other (Record): The pattern result record to match.
266cc1dc7a3Sopenharmony_ci
267cc1dc7a3Sopenharmony_ci        Returns:
268cc1dc7a3Sopenharmony_ci            Record: The result, if present.
269cc1dc7a3Sopenharmony_ci
270cc1dc7a3Sopenharmony_ci        Raises:
271cc1dc7a3Sopenharmony_ci            KeyError: No match could be found.
272cc1dc7a3Sopenharmony_ci        """
273cc1dc7a3Sopenharmony_ci        for record in self.records:
274cc1dc7a3Sopenharmony_ci            if record.blkSz == other.blkSz and record.name == other.name:
275cc1dc7a3Sopenharmony_ci                return record
276cc1dc7a3Sopenharmony_ci
277cc1dc7a3Sopenharmony_ci        raise KeyError()
278cc1dc7a3Sopenharmony_ci
279cc1dc7a3Sopenharmony_ci    def get_results_summary(self):
280cc1dc7a3Sopenharmony_ci        """
281cc1dc7a3Sopenharmony_ci        Get a results summary of all the records in this result set.
282cc1dc7a3Sopenharmony_ci
283cc1dc7a3Sopenharmony_ci        Returns:
284cc1dc7a3Sopenharmony_ci            ResultSummary: The result summary.
285cc1dc7a3Sopenharmony_ci        """
286cc1dc7a3Sopenharmony_ci        summary = ResultSummary()
287cc1dc7a3Sopenharmony_ci        for record in self.records:
288cc1dc7a3Sopenharmony_ci            summary.add_record(record)
289cc1dc7a3Sopenharmony_ci
290cc1dc7a3Sopenharmony_ci        return summary
291cc1dc7a3Sopenharmony_ci
292cc1dc7a3Sopenharmony_ci    def save_to_file(self, filePath):
293cc1dc7a3Sopenharmony_ci        """
294cc1dc7a3Sopenharmony_ci        Save this result set to a CSV file.
295cc1dc7a3Sopenharmony_ci
296cc1dc7a3Sopenharmony_ci        Args:
297cc1dc7a3Sopenharmony_ci            filePath (str): The output file path.
298cc1dc7a3Sopenharmony_ci        """
299cc1dc7a3Sopenharmony_ci        dirName = os.path.dirname(filePath)
300cc1dc7a3Sopenharmony_ci        if not os.path.exists(dirName):
301cc1dc7a3Sopenharmony_ci            os.makedirs(dirName)
302cc1dc7a3Sopenharmony_ci
303cc1dc7a3Sopenharmony_ci        with open(filePath, "w", newline="") as csvfile:
304cc1dc7a3Sopenharmony_ci            writer = csv.writer(csvfile)
305cc1dc7a3Sopenharmony_ci            self._save_header(writer)
306cc1dc7a3Sopenharmony_ci            for record in self.records:
307cc1dc7a3Sopenharmony_ci                self._save_record(writer, record)
308cc1dc7a3Sopenharmony_ci
309cc1dc7a3Sopenharmony_ci    @staticmethod
310cc1dc7a3Sopenharmony_ci    def _save_header(writer):
311cc1dc7a3Sopenharmony_ci        """
312cc1dc7a3Sopenharmony_ci        Write the header to the CSV file.
313cc1dc7a3Sopenharmony_ci
314cc1dc7a3Sopenharmony_ci        Args:
315cc1dc7a3Sopenharmony_ci            writer (csv.writer): The CSV writer.
316cc1dc7a3Sopenharmony_ci        """
317cc1dc7a3Sopenharmony_ci        row = ["Image Set", "Block Size", "Name",
318cc1dc7a3Sopenharmony_ci               "PSNR", "Total Time", "Coding Time", "Coding Rate"]
319cc1dc7a3Sopenharmony_ci        writer.writerow(row)
320cc1dc7a3Sopenharmony_ci
321cc1dc7a3Sopenharmony_ci    def _save_record(self, writer, record):
322cc1dc7a3Sopenharmony_ci        """
323cc1dc7a3Sopenharmony_ci        Write a record to the CSV file.
324cc1dc7a3Sopenharmony_ci
325cc1dc7a3Sopenharmony_ci        Args:
326cc1dc7a3Sopenharmony_ci            writer (csv.writer): The CSV writer.
327cc1dc7a3Sopenharmony_ci            record (Record): The record to write.
328cc1dc7a3Sopenharmony_ci        """
329cc1dc7a3Sopenharmony_ci        row = [self.testSet,
330cc1dc7a3Sopenharmony_ci               record.blkSz,
331cc1dc7a3Sopenharmony_ci               record.name,
332cc1dc7a3Sopenharmony_ci               "%0.4f" % record.psnr,
333cc1dc7a3Sopenharmony_ci               "%0.4f" % record.tTime,
334cc1dc7a3Sopenharmony_ci               "%0.4f" % record.cTime,
335cc1dc7a3Sopenharmony_ci               "%0.4f" % record.cRate]
336cc1dc7a3Sopenharmony_ci        writer.writerow(row)
337cc1dc7a3Sopenharmony_ci
338cc1dc7a3Sopenharmony_ci    def load_from_file(self, filePath):
339cc1dc7a3Sopenharmony_ci        """
340cc1dc7a3Sopenharmony_ci        Load a reference result set from a CSV file on disk.
341cc1dc7a3Sopenharmony_ci
342cc1dc7a3Sopenharmony_ci        Args:
343cc1dc7a3Sopenharmony_ci            filePath (str): The input file path.
344cc1dc7a3Sopenharmony_ci        """
345cc1dc7a3Sopenharmony_ci        with open(filePath, "r") as csvfile:
346cc1dc7a3Sopenharmony_ci            reader = csv.reader(csvfile)
347cc1dc7a3Sopenharmony_ci            # Skip the header
348cc1dc7a3Sopenharmony_ci            next(reader)
349cc1dc7a3Sopenharmony_ci            for row in reader:
350cc1dc7a3Sopenharmony_ci                assert row[0] == self.testSet
351cc1dc7a3Sopenharmony_ci                record = Record(row[1], row[2],
352cc1dc7a3Sopenharmony_ci                                float(row[3]), float(row[4]),
353cc1dc7a3Sopenharmony_ci                                float(row[5]), float(row[6]))
354cc1dc7a3Sopenharmony_ci                self.add_record(record)
355