1# Copyright 2019 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Unit tests for power telemetry utils.""" 6 7import unittest 8 9import power_telemetry_utils 10 11 12class TestInterpolateData(unittest.TestCase): 13 """Collection of tests to test smooten_data function in utils.""" 14 15 def test_Interpolate(self): 16 """Test that regular smoothening of data works.""" 17 data = [1.2, 3.6, float('nan'), float('nan'), 2.7] 18 expected_interp_data = [1.2, 3.6, 3.3, 3.0, 2.7] 19 interp_data = power_telemetry_utils.interpolate_missing_data(data) 20 self.assertListEqual(interp_data, expected_interp_data) 21 22 def test_InterpolateAllNaN(self): 23 """Test that a full NaN array cannot be smoothed.""" 24 data = [float('nan'), float('nan'), float('nan'), float('nan')] 25 with self.assertRaisesRegexp(power_telemetry_utils.TelemetryUtilsError, 26 'Data has no valid readings.'): 27 power_telemetry_utils.interpolate_missing_data(data) 28 29 def test_InterpolateGapStartAtBeginning(self): 30 """Test that a gap starting at the start gets the first known value.""" 31 data = [float('nan'), float('nan'), 2.6] 32 expected_interp_data = [2.6, 2.6, 2.6] 33 interp_data = power_telemetry_utils.interpolate_missing_data(data) 34 self.assertListEqual(interp_data, expected_interp_data) 35 36 def test_InterpolateGapEndsAtEnd(self): 37 """Test that a gap that ends at the end receives the last known value.""" 38 data = [2.6, float('nan'), float('nan')] 39 expected_interp_data = [2.6, 2.6, 2.6] 40 interp_data = power_telemetry_utils.interpolate_missing_data(data) 41 self.assertListEqual(interp_data, expected_interp_data) 42 43 def test_InterpolateTwoGaps(self): 44 """Test that two distinct gaps receive distinct values.""" 45 data = [2.6, float('nan'), 3.4, 2.0 , float('nan'), 2.5] 46 expected_interp_data = [2.6, 3.0, 3.4, 2.0, 2.25, 2.5] 47 interp_data = power_telemetry_utils.interpolate_missing_data(data) 48 self.assertListEqual(interp_data, expected_interp_data) 49 50 def test_InterpolateHandlesIntegerDivision(self): 51 """Test that integer division does not cause issues.""" 52 data = [2, float('nan'), 3] 53 expected_interp_data = [2, 2.5, 3] 54 interp_data = power_telemetry_utils.interpolate_missing_data(data) 55 self.assertListEqual(interp_data, expected_interp_data) 56 57 def test_AcceptableNaNRatio(self): 58 """Validation succeeds if the ratio of NaN is below the threshold.""" 59 data = [2, float('nan'), 3, 4, 5, 6] 60 # This should pass as there are only 1/6 NaN in the data. 61 max_nan_ratio = 0.3 62 args = {'max_nan_ratio': max_nan_ratio} 63 interp_data = power_telemetry_utils.interpolate_missing_data(data, 64 **args) 65 66 def test_ExcessiveNaNRatio(self): 67 """Validation fails if the ratio of NaN to valid readings is too high.""" 68 data = [2, float('nan'), 3, 4, 5, 6] 69 # This should fail as there are 1/6 NaN in the data. 70 max_nan_ratio = 0.1 71 args = {'max_nan_ratio': max_nan_ratio} 72 with self.assertRaisesRegexp(power_telemetry_utils.TelemetryUtilsError, 73 'NaN ratio of'): 74 interp_data = power_telemetry_utils.interpolate_missing_data(data, 75 **args) 76 77 def test_ExcessiveNaNSampleGap(self): 78 """Validation fails on too many consecutive NaN samples.""" 79 data = [2, float('nan'), float('nan'), float('nan'), 3, 4, 5, 6] 80 # This should fail as there is a 3 NaN gap. 81 max_sample_gap = 2 82 args = {'max_sample_gap': max_sample_gap} 83 with self.assertRaisesRegexp(power_telemetry_utils.TelemetryUtilsError, 84 'Too many consecutive NaN samples:'): 85 interp_data = power_telemetry_utils.interpolate_missing_data(data, 86 **args) 87 88 def test_ExcessiveNaNSampleGapAtBeginning(self): 89 """Validation fails on too many consecutive NaN samples at the start.""" 90 data = [float('nan'), float('nan'), float('nan'), 2] 91 # This should fail as there is a 3 NaN gap. 92 max_sample_gap = 2 93 args = {'max_sample_gap': max_sample_gap} 94 with self.assertRaisesRegexp(power_telemetry_utils.TelemetryUtilsError, 95 'Too many consecutive NaN samples:'): 96 interp_data = power_telemetry_utils.interpolate_missing_data(data, 97 **args) 98 99 def test_ExcessiveNaNSampleGapAtEnd(self): 100 """Validation fails on too many consecutive NaN samples at the end.""" 101 data = [2, float('nan'), float('nan'), float('nan')] 102 # This should fail as there is a 3 NaN gap. 103 max_sample_gap = 2 104 args = {'max_sample_gap': max_sample_gap} 105 with self.assertRaisesRegexp(power_telemetry_utils.TelemetryUtilsError, 106 'Too many consecutive NaN samples:'): 107 interp_data = power_telemetry_utils.interpolate_missing_data(data, 108 **args) 109 110 def test_AcceptableNaNTimeSampleGap(self): 111 """Validation succeeds if NaN gap is below threshold given a timeline.""" 112 data = [2, float('nan'), float('nan'), 3, 4, 5, 6] 113 # Timeline is s for the data above. 114 timeline = [1, 4, 7, 10, 13, 16, 19] 115 # This should not fail as there is only 9s gap. 116 max_sample_time_gap = 10 117 args = {'max_sample_time_gap': max_sample_time_gap, 118 'timeline': timeline} 119 interp_data = power_telemetry_utils.interpolate_missing_data(data, **args) 120 121 def test_ExcessiveNaNTimeSampleGap(self): 122 """Validation fails if NaN gap is too long on a given timeline.""" 123 data = [2, float('nan'), float('nan'), 3, 4, 5, 6] 124 # Timeline is s for the data above. 125 timeline = [1, 4, 7, 10, 13, 16, 19] 126 # This should fail as there 9s of gap. 127 max_sample_time_gap = 8 128 args = {'max_sample_time_gap': max_sample_time_gap, 129 'timeline': timeline} 130 with self.assertRaisesRegexp(power_telemetry_utils.TelemetryUtilsError, 131 'Excessively long sample gap'): 132 interp_data = power_telemetry_utils.interpolate_missing_data(data, 133 **args) 134 135 def test_NaNTimeSampleGapRequiresTimeline(self): 136 """|timeline| arg is required if checking for sample gap time.""" 137 data = [2, float('nan'), float('nan'), 3, 4, 5, 6] 138 # Timeline is s for the data above. 139 timeline = [1, 4, 7, 10, 13, 16, 19] 140 # This should fail the timeline is not provided in the args but the check 141 # is requested. 142 max_sample_time_gap = 2 143 args = {'max_sample_time_gap': max_sample_time_gap} 144 with self.assertRaisesRegexp(power_telemetry_utils.TelemetryUtilsError, 145 'Supplying max_sample_time_gap'): 146 interp_data = power_telemetry_utils.interpolate_missing_data(data, 147 **args) 148