Autotest TKO parser inserts perf measurements into the tko_iteration_perf_value database table.

This change is part of a series of changes in which we're modifying how
autotest handles/stores perf test measurements, to better align with chrome
team's perf dashboard.

Previously, the autotest TKO parser was taught how to parse the
perf_measurements output files produced by chromeOS perf tests.  In the
current CL, this parsed perf information is now inserted into the
autotest results database (table tko_iteration_perf_value).  Situations in
which autotest may need to delete information from this table are also
handled (mirrored off of how autotest handles perf keyvals).

Note that tko_iteration_perf_value is not yet exposed through the TKO RPC
interface.  This is because the scripts that upload to the perf dashboard
currently extract perf values from the database directly.  In the future,
those scripts may change to use the RPC interface, at which point the
RPC interface will need to be enhanced to expose data from
tko_iteration_perf_value.

BUG=chromium:258230
TEST=Verified using a local AFE and device that platform_GesturesRegressionTest
     runs successfully with these changes, and the outputted perf measurements
     are properly stored into the local results database.  Also verified that
     job_serializer_unittest.py passes with these changes (assuming that
     tko/tko.proto is compiled, which the unittests require as a pre-requisite).

Change-Id: I00799f2b96c9b45f5acdd5949c8f3dea0cc0821e
Reviewed-on: https://gerrit.chromium.org/gerrit/63232
Commit-Queue: Dennis Jeffrey <dennisjeffrey@chromium.org>
Reviewed-by: Dennis Jeffrey <dennisjeffrey@chromium.org>
Tested-by: Dennis Jeffrey <dennisjeffrey@chromium.org>
diff --git a/tko/db.py b/tko/db.py
index 6a2eb13..140a0f8 100644
--- a/tko/db.py
+++ b/tko/db.py
@@ -317,6 +317,7 @@
         for test_idx in self.find_tests(job_idx):
             where = {'test_idx' : test_idx}
             self.delete('tko_iteration_result', where)
+            self.delete('tko_iteration_perf_value', where)
             self.delete('tko_iteration_attributes', where)
             self.delete('tko_test_attributes', where)
             self.delete('tko_test_labels_tests', {'test_id': test_idx})
@@ -380,6 +381,7 @@
                         {'test_idx': test_idx}, commit=commit)
             where = {'test_idx': test_idx}
             self.delete('tko_iteration_result', where)
+            self.delete('tko_iteration_perf_value', where)
             self.delete('tko_iteration_attributes', where)
             where['user_created'] = 0
             self.delete('tko_test_attributes', where)
@@ -401,6 +403,17 @@
                 self.insert('tko_iteration_result', data,
                             commit=commit)
 
+        data = {'test_idx': test_idx}
+        for i in test.perf_values:
+            data['iteration'] = i.index
+            for perf_dict in i.perf_measurements:
+                data['description'] = perf_dict['description']
+                data['value'] = perf_dict['value']
+                data['stddev'] = perf_dict['stddev']
+                data['units'] = perf_dict['units']
+                data['higher_is_better'] = perf_dict['higher_is_better']
+                self.insert('tko_iteration_perf_value', data, commit=commit)
+
         for key, value in test.attributes.iteritems():
             data = {'test_idx': test_idx, 'attribute': key,
                     'value': value}
diff --git a/tko/job_serializer.py b/tko/job_serializer.py
index f950206..3999e9d 100755
--- a/tko/job_serializer.py
+++ b/tko/job_serializer.py
@@ -218,6 +218,16 @@
 
         fields_dict['labels'] = list(test.labels)
 
+        # The constructor for models.test accepts a "perf_values" list that
+        # represents performance values of the test.  The empty list argument
+        # in the constructor call below represents this value and makes this
+        # code adhere properly to the models.test constructor argument list.
+        # However, the effect of the empty list is that perf values are
+        # ignored in the job_serializer module.  This is ok for now because
+        # autotest does not use the current module.  If job_serializer is used
+        # in the future, we need to modify the "tko.proto" protobuf file to
+        # understand the notion of perf_values, then modify this file
+        # accordingly to use it.
         return models.test(fields_dict['subdir'],
                            fields_dict['testname'],
                            fields_dict['status'],
@@ -228,6 +238,7 @@
                            fields_dict['finished_time'],
                            fields_dict['iterations'],
                            fields_dict['attributes'],
+                           [],
                            fields_dict['labels'])
 
 
diff --git a/tko/job_serializer_unittest.py b/tko/job_serializer_unittest.py
index 7cad892..ef54ca5 100755
--- a/tko/job_serializer_unittest.py
+++ b/tko/job_serializer_unittest.py
@@ -42,11 +42,13 @@
 
         tko_labels = ['unittest', 'dummy test', 'autotest']
 
+        # See the comment about the models.test constructor in
+        # job_serializer.py, where the models.test constructor is used.
         tko_test = models.test('/tmp/', 'mocktest', 'Completed', 'N/A',
                                tko_kernel, 'My Computer', tko_time,
                                tko_time, [tko_iteration,
                                tko_iteration, tko_iteration],
-                               {'abc':'def'}, tko_labels)
+                               {'abc':'def'}, [], tko_labels)
 
         self.tko_job = tko_job
         self.tko_job.tests = [tko_test, tko_test, tko_test]
diff --git a/tko/models.py b/tko/models.py
index 4f5122a..f8b0c94 100644
--- a/tko/models.py
+++ b/tko/models.py
@@ -91,7 +91,7 @@
 
     def __init__(self, subdir, testname, status, reason, test_kernel,
                  machine, started_time, finished_time, iterations,
-                 attributes, labels):
+                 attributes, perf_values, labels):
         self.subdir = subdir
         self.testname = testname
         self.status = status
@@ -102,6 +102,7 @@
         self.finished_time = finished_time
         self.iterations = iterations
         self.attributes = attributes
+        self.perf_values = perf_values
         self.labels = labels
 
 
@@ -186,11 +187,10 @@
                 return existing_instance
         else:
             constructor = cls
-        # TODO(dennisjeffrey): pass the |perf_values| list to the call below
-        # so that the results can be written into the results database.
+
         return constructor(subdir, testname, status, reason, test_kernel,
                            job.machine, started_time, finished_time,
-                           iterations, attributes, [])
+                           iterations, attributes, perf_values, [])
 
 
     @classmethod
@@ -217,7 +217,7 @@
         tko_utils.dprint('parsing partial test %s %s' % (subdir, testname))
 
         return cls(subdir, testname, 'RUNNING', reason, test_kernel,
-                   job.machine, started_time, None, [], {}, [])
+                   job.machine, started_time, None, [], {}, [], [])
 
 
     @staticmethod
diff --git a/tko/parse.py b/tko/parse.py
index 90a7501..1bf0217 100755
--- a/tko/parse.py
+++ b/tko/parse.py
@@ -131,6 +131,7 @@
         for test_idx in old_tests.itervalues():
             where = {'test_idx' : test_idx}
             db.delete('tko_iteration_result', where)
+            db.delete('tko_iteration_perf_value', where)
             db.delete('tko_iteration_attributes', where)
             db.delete('tko_test_attributes', where)
             db.delete('tko_test_labels_tests', {'test_id': test_idx})