diff options
author | Hector Tellez <htellez@google.com> | 2016-03-04 19:56:20 -0800 |
---|---|---|
committer | Hector Tellez <htellez@google.com> | 2016-03-09 17:07:51 -0800 |
commit | e952089725398c8fa8b617ca7bdde840a38849fd (patch) | |
tree | 99028f4322d29b98ebc4dd40962484f4b98d2d7d | |
parent | 61a8a07e6770a403ebdb2d55dffcb33409573357 (diff) | |
download | loganalysis-e952089725398c8fa8b617ca7bdde840a38849fd.tar.gz |
Adds projected battery life analysis.
Change-Id: I25bcb84f5d62802dcb51d9a3ceae60929eb89606
5 files changed, 429 insertions, 3 deletions
diff --git a/src/com/android/loganalysis/item/BatteryDischargeStatsInfoItem.java b/src/com/android/loganalysis/item/BatteryDischargeStatsInfoItem.java new file mode 100644 index 0000000..8c15b92 --- /dev/null +++ b/src/com/android/loganalysis/item/BatteryDischargeStatsInfoItem.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.loganalysis.item; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * An {@link IItem} used to store information of the battery discharge. + */ +public class BatteryDischargeStatsInfoItem extends GenericItem { + + /** Constant for JSON output */ + public static final String MAX_PERCENTAGE = "MAX_PERCENTAGE"; + /** Constant for JSON output */ + public static final String MIN_PERCENTAGE = "MIN_PERCENTAGE"; + /** Constant for JSON output */ + public static final String DISCHARGE_PERCENTAGE = "DISCHARGE_PERCENTAGE"; + /** Constant for JSON output */ + public static final String DISCHARGE_DURATION = "DISCHARGE_DURATION"; + /** Constant for JSON output */ + public static final String PROJECTED_BATTERY_LIFE = "PROJECTED_BATTERY_LIFE"; + + private static final Set<String> ATTRIBUTES = new HashSet<>(Arrays.asList(MAX_PERCENTAGE, + MIN_PERCENTAGE, DISCHARGE_PERCENTAGE, DISCHARGE_DURATION, PROJECTED_BATTERY_LIFE)); + + /** + * The constructor for {@link BatteryDischargeStatsInfoItem}. + */ + public BatteryDischargeStatsInfoItem() { + super(ATTRIBUTES); + } + + /** + * Set the maximum percentage. + */ + public void setMaxPercentage(int percentage) { + setAttribute(MAX_PERCENTAGE, percentage); + } + + /** + * Set the minimum percentage. + */ + public void setMinPercentage(int percentage) { + setAttribute(MIN_PERCENTAGE, percentage); + } + + /** + * Set the discharge percentage. + */ + public void setDischargePercentage(int dischargePercentage) { + setAttribute(DISCHARGE_PERCENTAGE, dischargePercentage); + } + + /** + * Set the discharge duration. + */ + public void setDischargeDuration(long dischargeDuration) { + setAttribute(DISCHARGE_DURATION, dischargeDuration); + } + + /** + * Set the projected battery life. + */ + public void setProjectedBatteryLife(long projectedBatteryLife) { + setAttribute(PROJECTED_BATTERY_LIFE, projectedBatteryLife); + } + + /** + * Get the maximum percentage. + */ + public int getMaxPercentage() { + return (int) getAttribute(MAX_PERCENTAGE); + } + + /** + * Get the minimum percentage. + */ + public int getMinPercentage() { + return (int) getAttribute(MIN_PERCENTAGE); + } + + /** + * Get the discharge percentage. + */ + public int getDischargePercentage() { + return (int) getAttribute(DISCHARGE_PERCENTAGE); + } + + /** + * Get the discharge duration. + */ + public long getDischargeDuration() { + return (long) getAttribute(DISCHARGE_DURATION); + } + + /** + * Get the projected battery life. + */ + public long getProjectedBatteryLife() { + return (long) getAttribute(PROJECTED_BATTERY_LIFE); + } + +} diff --git a/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java b/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java index 1039324..8b7a800 100644 --- a/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java +++ b/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java @@ -27,9 +27,12 @@ public class DumpsysBatteryStatsItem implements IItem { public static final String SUMMARY = "SUMMARY"; /** Constant for JSON output */ public static final String DETAILED_STATS = "DETAILED_STATS"; + /** Constant for JSON output */ + public static final String DISCHARGE_STATS = "DISCHARGE_STATS"; private BatteryStatsSummaryInfoItem mBatteryStatsSummaryItem; private BatteryStatsDetailedInfoItem mDetailedBatteryStatsItem; + private BatteryDischargeStatsInfoItem mDischargeStatsItem; /** * Set the battery stats summary {@link BatteryStatsSummaryInfoItem} @@ -46,6 +49,13 @@ public class DumpsysBatteryStatsItem implements IItem { } /** + * Set the battery steps info item {@link BatteryDischargeStatsInfoItem} + */ + public void setBatteryDischargeStatsItem(BatteryDischargeStatsInfoItem item){ + mDischargeStatsItem = item; + } + + /** * Get the battery stats summary {@link BatteryStatsSummaryInfoItem} */ public BatteryStatsSummaryInfoItem getBatteryStatsSummaryItem() { @@ -59,6 +69,12 @@ public class DumpsysBatteryStatsItem implements IItem { return mDetailedBatteryStatsItem; } + /** + * Get the battery steps info item {@link BatteryDischargeStatsInfoItem} + */ + public BatteryDischargeStatsInfoItem getBatteryDischargeStatsItem() { + return mDischargeStatsItem; + } /** * {@inheritDoc} @@ -89,6 +105,9 @@ public class DumpsysBatteryStatsItem implements IItem { if (mDetailedBatteryStatsItem != null) { batteryStatsComponent.put(DETAILED_STATS, mDetailedBatteryStatsItem.toJson()); } + if (mDischargeStatsItem != null) { + batteryStatsComponent.put(DISCHARGE_STATS, mDischargeStatsItem.toJson()); + } } catch (JSONException e) { // ignore } diff --git a/src/com/android/loganalysis/parser/BatteryDischargeStatsInfoParser.java b/src/com/android/loganalysis/parser/BatteryDischargeStatsInfoParser.java new file mode 100644 index 0000000..00e0527 --- /dev/null +++ b/src/com/android/loganalysis/parser/BatteryDischargeStatsInfoParser.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.BatteryDischargeStatsInfoItem; +import com.android.loganalysis.item.BatteryStatsSummaryInfoItem; +import com.android.loganalysis.util.NumberFormattingUtil; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A {@link IParser} to parse the battery discharge section. + */ +public class BatteryDischargeStatsInfoParser extends AbstractSectionParser { + + /** + * Matches; #47: +5m5s105ms to 47 (screen-on, power-save-off, device-idle-off) + */ + private static Pattern DISCHARGE_STEP_PATTERN = Pattern + .compile("^.*: \\+((\\d+)h)?((\\d+)m)?((\\d+)s)?(\\d+)ms.* to (\\d+).*"); + + /** + * {@inheritDoc} + * + * @return The {@link BatteryDischargeStatsInfoItem}. + */ + @Override + public BatteryDischargeStatsInfoItem parse(List<String> lines) { + long totalDuration = 0; + long projectionDuration = 0; + Integer minPercent = null; + Integer maxPercent = null; + Integer minProjectionPercent = null; + Integer maxProjectionPercent = null; + + for (String line : lines) { + Matcher m = DISCHARGE_STEP_PATTERN.matcher(line); + + if (m.matches()) { + int percent = Integer.parseInt(m.group(8)); + + if (minPercent == null || percent < minPercent) { + minPercent = percent; + } + + if (maxPercent == null || maxPercent < percent) { + maxPercent = percent; + } + + long duration = NumberFormattingUtil.getMs( + NumberFormattingUtil.parseIntOrZero(m.group(2)), + NumberFormattingUtil.parseIntOrZero(m.group(4)), + NumberFormattingUtil.parseIntOrZero(m.group(6)), + NumberFormattingUtil.parseIntOrZero(m.group(7))); + + totalDuration += duration; + + // For computing the projected battery life we drop the first 5% of the battery + // charge because these discharge 'slower' and are not reliable for the projection. + if (percent > 94) { + continue; + } + + if (minProjectionPercent == null || percent < minProjectionPercent) { + minProjectionPercent = percent; + } + + if (maxProjectionPercent == null || maxProjectionPercent < percent) { + maxProjectionPercent = percent; + } + + projectionDuration += duration; + } + } + + if (minPercent == null) { + return null; + } + + int dischargePercent = maxPercent - minPercent + 1; + + BatteryDischargeStatsInfoItem item = new BatteryDischargeStatsInfoItem(); + item.setDischargeDuration(totalDuration); + item.setDischargePercentage(dischargePercent); + item.setMaxPercentage(maxPercent); + item.setMinPercentage(minPercent); + + if (minProjectionPercent == null) { + return item; + } + + int projectionDischargePercent = maxProjectionPercent - minProjectionPercent + 1; + item.setProjectedBatteryLife((projectionDuration * 100) / projectionDischargePercent); + return item; + } +} diff --git a/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java b/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java index 8a995e8..a93c892 100644 --- a/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java +++ b/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java @@ -16,6 +16,7 @@ package com.android.loganalysis.parser; +import com.android.loganalysis.item.BatteryDischargeStatsInfoItem; import com.android.loganalysis.item.BatteryStatsDetailedInfoItem; import com.android.loganalysis.item.DumpsysBatteryStatsItem; import com.android.loganalysis.item.BatteryStatsSummaryInfoItem; @@ -31,11 +32,17 @@ public class DumpsysBatteryStatsParser extends AbstractSectionParser { private static final String SUMMARY_INFO_SECTION_REGEX = "Battery History \\(\\d+% used, \\d+(KB)? used of \\d+KB, \\d+ strings using " + "\\d+(KB)?\\):$"; + private static final String DISCHARGE_STATS_INFO_SECTION_REGEX = "^Discharge step durations:$"; private static final String DETAILED_INFO_SECTION_REGEX = "^Statistics since last charge:$"; - private static final String NOOP_SECTION_REGEX = "^Statistics since last unplugged:$"; + + // We are not using this sections and will be ignored. + private static final String NOOP_SECTION_REGEX = + "^(Statistics since last unplugged:|Daily stats:)$"; private BatteryStatsSummaryInfoParser mSummaryParser = new BatteryStatsSummaryInfoParser(); private BatteryStatsDetailedInfoParser mDetailedParser = new BatteryStatsDetailedInfoParser(); + private BatteryDischargeStatsInfoParser mDischargeStepsParser = new + BatteryDischargeStatsInfoParser(); private DumpsysBatteryStatsItem mDumpsysBatteryStatsItem = null; private boolean mParsedInput = false; @@ -64,6 +71,7 @@ public class DumpsysBatteryStatsParser extends AbstractSectionParser { protected void setup() { addSectionParser(mSummaryParser, SUMMARY_INFO_SECTION_REGEX); addSectionParser(mDetailedParser, DETAILED_INFO_SECTION_REGEX); + addSectionParser(mDischargeStepsParser, DISCHARGE_STATS_INFO_SECTION_REGEX); addSectionParser(new NoopParser(), NOOP_SECTION_REGEX); } @@ -82,9 +90,11 @@ public class DumpsysBatteryStatsParser extends AbstractSectionParser { if (mDumpsysBatteryStatsItem != null) { mDumpsysBatteryStatsItem.setBatteryStatsSummarytem( - (BatteryStatsSummaryInfoItem) getSection(mSummaryParser)); + (BatteryStatsSummaryInfoItem) getSection(mSummaryParser)); mDumpsysBatteryStatsItem.setDetailedBatteryStatsItem( - (BatteryStatsDetailedInfoItem) getSection(mDetailedParser)); + (BatteryStatsDetailedInfoItem) getSection(mDetailedParser)); + mDumpsysBatteryStatsItem.setBatteryDischargeStatsItem( + (BatteryDischargeStatsInfoItem) getSection(mDischargeStepsParser)); } } } diff --git a/tests/src/com/android/loganalysis/parser/BatteryDischargeStatsInfoParserTest.java b/tests/src/com/android/loganalysis/parser/BatteryDischargeStatsInfoParserTest.java new file mode 100644 index 0000000..983e3aa --- /dev/null +++ b/tests/src/com/android/loganalysis/parser/BatteryDischargeStatsInfoParserTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.BatteryDischargeStatsInfoItem; + +import junit.framework.TestCase; + +import org.json.JSONException; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link BatteryDischargeStatsInfoParser} + */ +public class BatteryDischargeStatsInfoParserTest extends TestCase { + + /** + * Test that normal input is parsed correctly. + */ + public void testBatteryDischargeStats() { + List<String> input = Arrays.asList( + " #0: +4m53s738ms to 0 (screen-on, power-save-off, device-idle-off)", + " #1: +4m5s586ms to 1 (screen-on, power-save-off, device-idle-off)", + " #2: +3m0s157ms to 2 (screen-on, power-save-off, device-idle-off)", + " #3: +2m52s243ms to 3 (screen-on, power-save-off, device-idle-off)", + " #4: +2m27s599ms to 4 (screen-on, power-save-off, device-idle-off)", + " #5: +5m0s172ms to 5 (screen-on, power-save-off, device-idle-off)", + " #6: +2m21s664ms to 6 (screen-on, power-save-off, device-idle-off)", + " #7: +5m18s811ms to 7 (screen-on, power-save-off, device-idle-off)", + " #8: +3m35s622ms to 8 (screen-on, power-save-off, device-idle-off)", + " #9: +4m52s605ms to 9 (screen-on, power-save-off, device-idle-off)", + " #10: +4m46s779ms to 10 (screen-on, power-save-off, device-idle-off)", + " #11: +4m0s200ms to 11 (screen-on, power-save-off, device-idle-off)", + " #12: +4m44s941ms to 12 (screen-on, power-save-off, device-idle-off)", + " #13: +3m31s163ms to 13 (screen-on, power-save-off, device-idle-off)", + " #14: +4m17s293ms to 14 (screen-on, power-save-off, device-idle-off)", + " #15: +3m11s584ms to 15 (screen-on, power-save-off, device-idle-off)", + " #16: +5m52s923ms to 16 (screen-on, power-save-off, device-idle-off)", + " #17: +3m7s34ms to 17 (screen-on, power-save-off, device-idle-off)", + " #18: +5m59s810ms to 18 (screen-on, power-save-off, device-idle-off)", + " #19: +6m15s275ms to 19 (screen-on, power-save-off, device-idle-off)", + " #20: +4m0s55ms to 20 (screen-on, power-save-off, device-idle-off)", + " #21: +5m21s911ms to 21 (screen-on, power-save-off, device-idle-off)", + " #22: +4m0s171ms to 22 (screen-on, power-save-off, device-idle-off)", + " #23: +5m22s820ms to 23 (screen-on, power-save-off, device-idle-off)", + " #24: +3m44s752ms to 24 (screen-on, power-save-off, device-idle-off)", + " #25: +4m15s130ms to 25 (screen-on, power-save-off, device-idle-off)", + " #26: +3m48s654ms to 26 (screen-on, power-save-off, device-idle-off)", + " #27: +5m0s294ms to 27 (screen-on, power-save-off, device-idle-off)", + " #28: +3m11s169ms to 28 (screen-on, power-save-off, device-idle-off)", + " #29: +4m48s194ms to 29 (screen-on, power-save-off, device-idle-off)", + " #30: +5m0s319ms to 30 (screen-on, power-save-off, device-idle-off)", + " #31: +2m42s209ms to 31 (screen-on, power-save-off, device-idle-off)", + " #32: +5m29s187ms to 32 (screen-on, power-save-off, device-idle-off)", + " #33: +3m32s392ms to 33 (screen-on, power-save-off, device-idle-off)", + " #34: +5m27s578ms to 34 (screen-on, power-save-off, device-idle-off)", + " #35: +3m47s37ms to 35 (screen-on, power-save-off, device-idle-off)", + " #36: +5m18s916ms to 36 (screen-on, power-save-off, device-idle-off)", + " #37: +2m54s111ms to 37 (screen-on, power-save-off, device-idle-off)", + " #38: +6m32s480ms to 38 (screen-on, power-save-off, device-idle-off)", + " #39: +5m24s906ms to 39 (screen-on, power-save-off, device-idle-off)", + " #40: +3m2s451ms to 40 (screen-on, power-save-off, device-idle-off)", + " #41: +6m29s762ms to 41 (screen-on, power-save-off, device-idle-off)", + " #42: +3m31s933ms to 42 (screen-on, power-save-off, device-idle-off)", + " #43: +4m58s520ms to 43 (screen-on, power-save-off, device-idle-off)", + " #44: +4m31s130ms to 44 (screen-on, power-save-off, device-idle-off)", + " #45: +5m28s870ms to 45 (screen-on, power-save-off, device-idle-off)", + " #46: +3m54s809ms to 46 (screen-on, power-save-off, device-idle-off)", + " #47: +5m5s105ms to 47 (screen-on, power-save-off, device-idle-off)", + " #48: +3m50s427ms to 48 (screen-on, power-save-off, device-idle-off)", + " #49: +6m0s344ms to 49 (screen-on, power-save-off, device-idle-off)", + " #50: +5m2s952ms to 50 (screen-on, power-save-off, device-idle-off)", + " #51: +3m6s120ms to 51 (screen-on, power-save-off, device-idle-off)", + " #52: +5m34s839ms to 52 (screen-on, power-save-off, device-idle-off)", + " #53: +2m33s473ms to 53 (screen-on, power-save-off, device-idle-off)", + " #54: +4m51s873ms to 54 (screen-on, power-save-off, device-idle-off)", + " #55: +3m30s41ms to 55 (screen-on, power-save-off, device-idle-off)", + " #56: +4m29s879ms to 56 (screen-on, power-save-off, device-idle-off)", + " #57: +3m41s722ms to 57 (screen-on, power-save-off, device-idle-off)", + " #58: +4m29s72ms to 58 (screen-on, power-save-off, device-idle-off)", + " #59: +4m49s351ms to 59 (screen-on, power-save-off, device-idle-off)", + " #60: +3m51s605ms to 60 (screen-on, power-save-off, device-idle-off)", + " #61: +5m8s334ms to 61 (screen-on, power-save-off, device-idle-off)", + " #62: +2m53s153ms to 62 (screen-on, power-save-off, device-idle-off)", + " #63: +6m0s234ms to 63 (screen-on, power-save-off, device-idle-off)", + " #64: +3m20s345ms to 64 (screen-on, power-save-off, device-idle-off)", + " #65: +5m46s211ms to 65 (screen-on, power-save-off, device-idle-off)", + " #66: +3m40s147ms to 66 (screen-on, power-save-off, device-idle-off)", + " #67: +5m14s559ms to 67 (screen-on, power-save-off, device-idle-off)", + " #68: +4m0s183ms to 68 (screen-on, power-save-off, device-idle-off)", + " #69: +5m23s334ms to 69 (screen-on, power-save-off, device-idle-off)", + " #70: +5m45s493ms to 70 (screen-on, power-save-off, device-idle-off)", + " #71: +4m0s179ms to 71 (screen-on, power-save-off, device-idle-off)", + " #72: +5m45s462ms to 72 (screen-on, power-save-off, device-idle-off)", + " #73: +3m10s449ms to 73 (screen-on, power-save-off, device-idle-off)", + " #74: +6m29s370ms to 74 (screen-on, power-save-off, device-idle-off)", + " #75: +3m20s414ms to 75 (screen-on, power-save-off, device-idle-off)", + " #76: +5m10s462ms to 76 (screen-on, power-save-off, device-idle-off)", + " #77: +4m20s500ms to 77 (screen-on, power-save-off, device-idle-off)", + " #78: +5m39s504ms to 78 (screen-on, power-save-off, device-idle-off)", + " #79: +5m59s819ms to 79 (screen-on, power-save-off, device-idle-off)", + " #80: +3m0s126ms to 80 (screen-on, power-save-off, device-idle-off)", + " #81: +6m20s912ms to 81 (screen-on, power-save-off, device-idle-off)", + " #82: +4m0s199ms to 82 (screen-on, power-save-off, device-idle-off)", + " #83: +5m23s470ms to 83 (screen-on, power-save-off, device-idle-off)", + " #84: +3m15s368ms to 84 (screen-on, power-save-off, device-idle-off)", + " #85: +6m18s625ms to 85 (screen-on, power-save-off, device-idle-off)", + " #86: +3m41s417ms to 86 (screen-on, power-save-off, device-idle-off)", + " #87: +5m32s257ms to 87 (screen-on, power-save-off, device-idle-off)", + " #88: +4m0s212ms to 88 (screen-on, power-save-off, device-idle-off)", + " #89: +5m41s218ms to 89 (screen-on, power-save-off, device-idle-off)", + " #90: +5m46s333ms to 90 (screen-on, power-save-off, device-idle-off)", + " #91: +3m58s362ms to 91 (screen-on, power-save-off, device-idle-off)", + " #92: +5m1s593ms to 92 (screen-on, power-save-off, device-idle-off)", + " #93: +4m47s33ms to 93 (screen-on, power-save-off, device-idle-off)", + " #94: +6m0s417ms to 94 (screen-on, power-save-off, device-idle-off)", + " #95: +3m9s77ms to 95 (screen-on, power-save-off, device-idle-off)", + " #96: +7m0s308ms to 96 (screen-on, power-save-off, device-idle-off)", + " #97: +3m29s741ms to 97 (screen-on, power-save-off, device-idle-off)", + " #98: +7m12s748ms to 98 (screen-on, power-save-off, device-idle-off)", + " Estimated screen on time: 7h 36m 13s 0ms "); + + BatteryDischargeStatsInfoItem infoItem = new BatteryDischargeStatsInfoParser().parse(input); + assertEquals(99, infoItem.getDischargePercentage()); + assertEquals(27099330, infoItem.getDischargeDuration()); + assertEquals(27207848, infoItem.getProjectedBatteryLife()); + } + + /** + * Test that input with only a few discharge stats. + */ + public void testBatteryDischargeStatsWithTop5Percentages() throws JSONException { + List<String> input = Arrays.asList( + " #95: +3m9s77ms to 95 (screen-on, power-save-off, device-idle-off)", + " #96: +7m0s308ms to 96 (screen-on, power-save-off, device-idle-off)", + " #97: +3m29s741ms to 97 (screen-on, power-save-off, device-idle-off)", + " #98: +7m12s748ms to 98 (screen-on, power-save-off, device-idle-off)", + " Estimated screen on time: 7h 36m 13s 0ms "); + + BatteryDischargeStatsInfoItem infoItem = new BatteryDischargeStatsInfoParser().parse(input); + + try { + infoItem.getProjectedBatteryLife(); + fail("Projected battery life is expected to be undefined when there are not enough" + + " samples of battery discharge below 95 percent."); + } catch (NullPointerException e) { + // NullPointerException expected. + } + } +} |