1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.storagemonitoring;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyString;
26 import static org.mockito.Mockito.doAnswer;
27 import static org.mockito.Mockito.doReturn;
28 
29 import android.car.storagemonitoring.IoStats;
30 import android.car.storagemonitoring.IoStatsEntry;
31 import android.car.storagemonitoring.LifetimeWriteInfo;
32 import android.car.storagemonitoring.UidIoRecord;
33 import android.car.storagemonitoring.WearEstimate;
34 import android.car.storagemonitoring.WearEstimateChange;
35 import android.car.test.util.TemporaryFile;
36 import android.hardware.health.V2_0.IHealth;
37 import android.hardware.health.V2_0.IHealth.getStorageInfoCallback;
38 import android.hardware.health.V2_0.Result;
39 import android.hardware.health.V2_0.StorageInfo;
40 import android.os.Parcel;
41 import android.util.JsonReader;
42 import android.util.JsonWriter;
43 import android.util.SparseArray;
44 
45 import androidx.test.filters.MediumTest;
46 
47 import com.android.car.test.utils.TemporaryDirectory;
48 
49 import org.json.JSONObject;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.Mock;
53 import org.mockito.junit.MockitoJUnitRunner;
54 
55 import java.io.FileWriter;
56 import java.io.StringReader;
57 import java.io.StringWriter;
58 import java.nio.file.Files;
59 import java.time.Instant;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.Collections;
63 import java.util.List;
64 
65 /**
66  * Tests the storage monitoring API in CarService.
67  */
68 @RunWith(MockitoJUnitRunner.class)
69 @MediumTest
70 public class CarStorageMonitoringTest {
71     static final String TAG = CarStorageMonitoringTest.class.getSimpleName();
72 
73     @Mock private IHealth mMockedHal;
74     @Mock private HealthServiceWearInfoProvider.IHealthSupplier mHealthServiceSupplier;
75 
76     @Test
testEMmcWearInformationProvider()77     public void testEMmcWearInformationProvider() throws Exception {
78         try (TemporaryFile lifetimeFile = new TemporaryFile(TAG)) {
79             try (TemporaryFile eolFile = new TemporaryFile(TAG)) {
80                 lifetimeFile.write("0x05 0x00");
81                 eolFile.write("01");
82 
83                 EMmcWearInformationProvider wearInfoProvider = new EMmcWearInformationProvider(
84                         lifetimeFile.getFile(), eolFile.getFile());
85 
86                 WearInformation wearInformation = wearInfoProvider.load();
87 
88                 assertThat(wearInformation).isNotNull();
89                 assertThat(wearInformation.lifetimeEstimateA).isEqualTo(40);
90                 assertThat(wearInformation.lifetimeEstimateB)
91                     .isEqualTo(WearInformation.UNKNOWN_LIFETIME_ESTIMATE);
92                 assertThat(wearInformation.preEolInfo)
93                     .isEqualTo(WearInformation.PRE_EOL_INFO_NORMAL);
94             }
95         }
96     }
97 
98     @Test
testUfsWearInformationProvider()99     public void testUfsWearInformationProvider() throws Exception {
100         try (TemporaryFile lifetimeFile = new TemporaryFile(TAG)) {
101             lifetimeFile.write("ufs version: 1.0\n" +
102                     "Health Descriptor[Byte offset 0x2]: bPreEOLInfo = 0x2\n" +
103                     "Health Descriptor[Byte offset 0x1]: bDescriptionIDN = 0x1\n" +
104                     "Health Descriptor[Byte offset 0x3]: bDeviceLifeTimeEstA = 0x0\n" +
105                     "Health Descriptor[Byte offset 0x5]: VendorPropInfo = somedatahere\n" +
106                     "Health Descriptor[Byte offset 0x4]: bDeviceLifeTimeEstB = 0xA\n");
107 
108             UfsWearInformationProvider wearInfoProvider = new UfsWearInformationProvider(
109                 lifetimeFile.getFile());
110 
111             WearInformation wearInformation = wearInfoProvider.load();
112 
113             assertThat(wearInformation).isNotNull();
114             assertThat(wearInformation.lifetimeEstimateB).isEqualTo(90);
115             assertThat(wearInformation.preEolInfo).isEqualTo(WearInformation.PRE_EOL_INFO_WARNING);
116             assertThat(wearInformation.lifetimeEstimateA)
117                 .isEqualTo(WearInformation.UNKNOWN_LIFETIME_ESTIMATE);
118         }
119     }
120 
121     @Test
testHealthServiceWearInformationProvider()122     public void testHealthServiceWearInformationProvider() throws Exception {
123         StorageInfo storageInfo = new StorageInfo();
124         storageInfo.eol = WearInformation.PRE_EOL_INFO_NORMAL;
125         storageInfo.lifetimeA = 3;
126         storageInfo.lifetimeB = WearInformation.UNKNOWN_LIFETIME_ESTIMATE;
127         storageInfo.attr.isInternal = true;
128         HealthServiceWearInfoProvider wearInfoProvider = new HealthServiceWearInfoProvider();
129         wearInfoProvider.setHealthSupplier(mHealthServiceSupplier);
130 
131         doReturn(mMockedHal)
132             .when(mHealthServiceSupplier).get(anyString());
133         doAnswer((invocation) -> {
134             ArrayList<StorageInfo> list = new ArrayList<StorageInfo>();
135             list.add(storageInfo);
136             ((IHealth.getStorageInfoCallback) invocation.getArguments()[0])
137                 .onValues(Result.SUCCESS, list);
138             return null;
139         }).when(mMockedHal).getStorageInfo(any(getStorageInfoCallback.class));
140         WearInformation wearInformation = wearInfoProvider.load();
141 
142         assertThat(wearInformation).isNotNull();
143         assertThat(wearInformation.lifetimeEstimateA).isEqualTo(20);
144         assertThat(wearInformation.lifetimeEstimateB).isEqualTo(storageInfo.lifetimeB);
145         assertThat(wearInformation.preEolInfo).isEqualTo(storageInfo.eol);
146     }
147 
148     @Test
149     @SuppressWarnings("TruthSelfEquals")
150     // TODO: use EqualsTester to check equality with itself,
151     // Remove @SuppressWarnings("TruthSelfEquals") at other places too
testWearEstimateEquality()152     public void testWearEstimateEquality() {
153         WearEstimate wearEstimate1 = new WearEstimate(10, 20);
154         WearEstimate wearEstimate2 = new WearEstimate(10, 20);
155         WearEstimate wearEstimate3 = new WearEstimate(20, 30);
156         assertThat(wearEstimate1).isEqualTo(wearEstimate1);
157         assertThat(wearEstimate2).isEqualTo(wearEstimate1);
158         assertThat(wearEstimate1).isNotSameInstanceAs(wearEstimate3);
159     }
160 
161     @Test
testWearEstimateParcel()162     public void testWearEstimateParcel() throws Exception {
163         WearEstimate originalWearEstimate = new WearEstimate(10, 20);
164         Parcel p = Parcel.obtain();
165         originalWearEstimate.writeToParcel(p, 0);
166         p.setDataPosition(0);
167         WearEstimate newWearEstimate = new WearEstimate(p);
168         assertThat(newWearEstimate).isEqualTo(originalWearEstimate);
169         p.recycle();
170     }
171 
172     @Test
173     @SuppressWarnings("TruthSelfEquals")
testWearEstimateChangeEquality()174     public void testWearEstimateChangeEquality() {
175         WearEstimateChange wearEstimateChange1 = new WearEstimateChange(
176                 new WearEstimate(10, 20),
177                 new WearEstimate(20, 30),
178                 5000L,
179                 Instant.now(),
180                 false);
181         WearEstimateChange wearEstimateChange2 = new WearEstimateChange(
182             new WearEstimate(10, 20),
183             new WearEstimate(20, 30),
184             5000L,
185             wearEstimateChange1.dateAtChange,
186             false);
187         assertThat(wearEstimateChange1).isEqualTo(wearEstimateChange1);
188         assertThat(wearEstimateChange2).isEqualTo(wearEstimateChange1);
189         WearEstimateChange wearEstimateChange3 = new WearEstimateChange(
190             new WearEstimate(10, 30),
191             new WearEstimate(20, 30),
192             3000L,
193             Instant.now(),
194             true);
195         assertThat(wearEstimateChange1).isNotSameInstanceAs(wearEstimateChange3);
196     }
197 
198     @Test
testWearEstimateChangeParcel()199     public void testWearEstimateChangeParcel() throws Exception {
200         WearEstimateChange originalWearEstimateChange = new WearEstimateChange(
201                 new WearEstimate(10, 0),
202                 new WearEstimate(20, 10),
203                 123456789L,
204                 Instant.now(),
205                 false);
206         Parcel p = Parcel.obtain();
207         originalWearEstimateChange.writeToParcel(p, 0);
208         p.setDataPosition(0);
209         WearEstimateChange newWearEstimateChange = new WearEstimateChange(p);
210         assertThat(newWearEstimateChange).isEqualTo(originalWearEstimateChange);
211         p.recycle();
212     }
213 
214     @Test
testWearEstimateJson()215     public void testWearEstimateJson() throws Exception {
216         WearEstimate originalWearEstimate = new WearEstimate(20, 0);
217         StringWriter stringWriter = new StringWriter(1024);
218         JsonWriter jsonWriter = new JsonWriter(stringWriter);
219         originalWearEstimate.writeToJson(jsonWriter);
220         StringReader stringReader = new StringReader(stringWriter.toString());
221         JsonReader jsonReader = new JsonReader(stringReader);
222         WearEstimate newWearEstimate = new WearEstimate(jsonReader);
223         assertThat(newWearEstimate).isEqualTo(originalWearEstimate);
224     }
225 
226     @Test
testWearEstimateRecordJson()227     public void testWearEstimateRecordJson() throws Exception {
228         try (TemporaryFile temporaryFile = new TemporaryFile(TAG)) {
229             WearEstimateRecord originalWearEstimateRecord = new WearEstimateRecord(new WearEstimate(10, 20),
230                 new WearEstimate(10, 30), 5000, Instant.ofEpochMilli(1000));
231             try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(temporaryFile.getFile()))) {
232                 originalWearEstimateRecord.writeToJson(jsonWriter);
233             }
234             JSONObject jsonObject = new JSONObject(
235                     new String(Files.readAllBytes(temporaryFile.getPath())));
236             WearEstimateRecord newWearEstimateRecord = new WearEstimateRecord(jsonObject);
237             assertThat(newWearEstimateRecord).isEqualTo(originalWearEstimateRecord);
238         }
239     }
240 
241     @Test
242     @SuppressWarnings("TruthSelfEquals")
testWearEstimateRecordEquality()243     public void testWearEstimateRecordEquality() throws Exception {
244         WearEstimateRecord wearEstimateRecord1 = new WearEstimateRecord(WearEstimate.UNKNOWN_ESTIMATE,
245                 new WearEstimate(10, 20), 5000, Instant.ofEpochMilli(2000));
246         WearEstimateRecord wearEstimateRecord2 = new WearEstimateRecord(WearEstimate.UNKNOWN_ESTIMATE,
247             new WearEstimate(10, 20), 5000, Instant.ofEpochMilli(2000));
248         WearEstimateRecord wearEstimateRecord3 = new WearEstimateRecord(WearEstimate.UNKNOWN_ESTIMATE,
249             new WearEstimate(10, 40), 5000, Instant.ofEpochMilli(1000));
250 
251         assertThat(wearEstimateRecord1).isEqualTo(wearEstimateRecord1);
252         assertThat(wearEstimateRecord2).isEqualTo(wearEstimateRecord1);
253         assertThat(wearEstimateRecord1).isNotSameInstanceAs(wearEstimateRecord3);
254     }
255 
256     @Test
testWearHistoryJson()257     public void testWearHistoryJson() throws Exception {
258         try (TemporaryFile temporaryFile = new TemporaryFile(TAG)) {
259             WearEstimateRecord wearEstimateRecord1 = new WearEstimateRecord(
260                 WearEstimate.UNKNOWN_ESTIMATE,
261                 new WearEstimate(10, 20), 5000, Instant.ofEpochMilli(2000));
262             WearEstimateRecord wearEstimateRecord2 = new WearEstimateRecord(
263                 wearEstimateRecord1.getOldWearEstimate(),
264                 new WearEstimate(10, 40), 9000, Instant.ofEpochMilli(16000));
265             WearEstimateRecord wearEstimateRecord3 = new WearEstimateRecord(
266                 wearEstimateRecord2.getOldWearEstimate(),
267                 new WearEstimate(20, 40), 12000, Instant.ofEpochMilli(21000));
268             WearHistory originalWearHistory = WearHistory.fromRecords(wearEstimateRecord1,
269                 wearEstimateRecord2, wearEstimateRecord3);
270             try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(temporaryFile.getFile()))) {
271                 originalWearHistory.writeToJson(jsonWriter);
272             }
273             JSONObject jsonObject = new JSONObject(
274                 new String(Files.readAllBytes(temporaryFile.getPath())));
275             WearHistory newWearHistory = new WearHistory(jsonObject);
276             assertThat(newWearHistory).isEqualTo(originalWearHistory);
277         }
278     }
279 
280     @Test
281     @SuppressWarnings("TruthSelfEquals")
testWearHistoryEquality()282     public void testWearHistoryEquality() throws Exception {
283         WearEstimateRecord wearEstimateRecord1 = new WearEstimateRecord(
284             WearEstimate.UNKNOWN_ESTIMATE,
285             new WearEstimate(10, 20), 5000, Instant.ofEpochMilli(2000));
286         WearEstimateRecord wearEstimateRecord2 = new WearEstimateRecord(
287             wearEstimateRecord1.getOldWearEstimate(),
288             new WearEstimate(10, 40), 9000, Instant.ofEpochMilli(16000));
289         WearEstimateRecord wearEstimateRecord3 = new WearEstimateRecord(
290             wearEstimateRecord2.getOldWearEstimate(),
291             new WearEstimate(20, 40), 12000, Instant.ofEpochMilli(21000));
292         WearEstimateRecord wearEstimateRecord4 = new WearEstimateRecord(
293             wearEstimateRecord3.getOldWearEstimate(),
294             new WearEstimate(30, 50), 17000, Instant.ofEpochMilli(34000));
295         WearEstimateRecord wearEstimateRecord5 = new WearEstimateRecord(
296             wearEstimateRecord3.getOldWearEstimate(),
297             new WearEstimate(20, 50), 15000, Instant.ofEpochMilli(34000));
298 
299         WearHistory wearHistory1 = WearHistory.fromRecords(wearEstimateRecord1,
300             wearEstimateRecord2, wearEstimateRecord3, wearEstimateRecord4);
301         WearHistory wearHistory2 = WearHistory.fromRecords(wearEstimateRecord4,
302             wearEstimateRecord1, wearEstimateRecord2, wearEstimateRecord3);
303         WearHistory wearHistory3 = WearHistory.fromRecords(wearEstimateRecord1,
304             wearEstimateRecord2, wearEstimateRecord3, wearEstimateRecord5);
305 
306         assertThat(wearHistory1).isEqualTo(wearHistory1);
307         assertThat(wearHistory2).isEqualTo(wearHistory1);
308         assertThat(wearHistory1).isNotSameInstanceAs(wearHistory3);
309     }
310 
311     @Test
testWearHistoryToChanges()312     public void testWearHistoryToChanges() {
313         WearEstimateRecord wearEstimateRecord1 = new WearEstimateRecord(
314             WearEstimate.UNKNOWN_ESTIMATE,
315             new WearEstimate(10, 20), 3600000, Instant.ofEpochMilli(2000));
316         WearEstimateRecord wearEstimateRecord2 = new WearEstimateRecord(
317             wearEstimateRecord1.getOldWearEstimate(),
318             new WearEstimate(10, 40), 172000000, Instant.ofEpochMilli(16000));
319         WearEstimateRecord wearEstimateRecord3 = new WearEstimateRecord(
320             wearEstimateRecord2.getOldWearEstimate(),
321             new WearEstimate(20, 40), 172000001, Instant.ofEpochMilli(21000));
322 
323         WearHistory wearHistory = WearHistory.fromRecords(wearEstimateRecord1,
324             wearEstimateRecord2, wearEstimateRecord3);
325 
326         List<WearEstimateChange> wearEstimateChanges = wearHistory.toWearEstimateChanges(1);
327 
328         assertThat(wearEstimateChanges.size()).isEqualTo(3);
329         WearEstimateChange unknownToOne = wearEstimateChanges.get(0);
330         WearEstimateChange oneToTwo = wearEstimateChanges.get(1);
331         WearEstimateChange twoToThree = wearEstimateChanges.get(2);
332 
333         assertThat(wearEstimateRecord1.getOldWearEstimate()).isEqualTo(unknownToOne.oldEstimate);
334         assertThat(wearEstimateRecord1.getNewWearEstimate()).isEqualTo(unknownToOne.newEstimate);
335         assertThat(wearEstimateRecord1.getTotalCarServiceUptime())
336             .isEqualTo(unknownToOne.uptimeAtChange);
337         assertThat(wearEstimateRecord1.getUnixTimestamp()).isEqualTo(unknownToOne.dateAtChange);
338         assertThat(unknownToOne.isAcceptableDegradation).isTrue();
339 
340         assertThat(wearEstimateRecord2.getOldWearEstimate()).isEqualTo(oneToTwo.oldEstimate);
341         assertThat(wearEstimateRecord2.getNewWearEstimate()).isEqualTo(oneToTwo.newEstimate);
342         assertThat(wearEstimateRecord2.getTotalCarServiceUptime())
343             .isEqualTo(oneToTwo.uptimeAtChange);
344         assertThat(wearEstimateRecord2.getUnixTimestamp()).isEqualTo(oneToTwo.dateAtChange);
345         assertThat(oneToTwo.isAcceptableDegradation).isTrue();
346 
347         assertThat(wearEstimateRecord3.getOldWearEstimate()).isEqualTo(twoToThree.oldEstimate);
348         assertThat(wearEstimateRecord3.getNewWearEstimate()).isEqualTo(twoToThree.newEstimate);
349         assertThat(wearEstimateRecord3.getTotalCarServiceUptime())
350             .isEqualTo(twoToThree.uptimeAtChange);
351         assertThat(wearEstimateRecord3.getUnixTimestamp()).isEqualTo(twoToThree.dateAtChange);
352         assertThat(twoToThree.isAcceptableDegradation).isFalse();
353     }
354 
355     @Test
testUidIoStatEntry()356     public void testUidIoStatEntry() throws Exception {
357         try (TemporaryFile statsFile = new TemporaryFile(TAG)) {
358             statsFile.write("0 256797495 181736102 362132480 947167232 0 0 0 0 250 0\n"
359                 + "1006 489007 196802 0 20480 51474 2048 1024 2048 1 1\n");
360 
361             ProcfsUidIoStatsProvider statsProvider = new ProcfsUidIoStatsProvider(
362                     statsFile.getPath());
363 
364             SparseArray<UidIoRecord> entries = statsProvider.load();
365 
366             assertThat(entries).isNotNull();
367             assertThat(entries.size()).isEqualTo(2);
368 
369             IoStatsEntry entry = new IoStatsEntry(entries.get(0), 1234);
370             assertThat(entry).isNotNull();
371             assertThat(entry.uid).isEqualTo(0);
372             assertThat(entry.runtimeMillis).isEqualTo(1234);
373             assertThat(entry.foreground.bytesRead).isEqualTo(256797495);
374             assertThat(entry.foreground.bytesWritten).isEqualTo(181736102);
375             assertThat(entry.foreground.bytesReadFromStorage).isEqualTo(362132480);
376             assertThat(entry.foreground.bytesWrittenToStorage).isEqualTo(947167232);
377             assertThat(entry.foreground.fsyncCalls).isEqualTo(250);
378             assertThat(entry.background.bytesRead).isEqualTo(0);
379             assertThat(entry.background.bytesWritten).isEqualTo(0);
380             assertThat(entry.background.bytesReadFromStorage).isEqualTo(0);
381             assertThat(entry.background.bytesWrittenToStorage).isEqualTo(0);
382             assertThat(entry.background.fsyncCalls).isEqualTo(0);
383 
384             entry = new IoStatsEntry(entries.get(1006), 4321);
385             assertThat(entry).isNotNull();
386             assertThat(entry.uid).isEqualTo(1006);
387             assertThat(entry.runtimeMillis).isEqualTo(4321);
388             assertThat(entry.foreground.bytesRead).isEqualTo(489007);
389             assertThat(entry.foreground.bytesWritten).isEqualTo(196802);
390             assertThat(entry.foreground.bytesReadFromStorage).isEqualTo(0);
391             assertThat(entry.foreground.bytesWrittenToStorage).isEqualTo(20480);
392             assertThat(entry.foreground.fsyncCalls).isEqualTo(1);
393             assertThat(entry.background.bytesRead).isEqualTo(51474);
394             assertThat(entry.background.bytesWritten).isEqualTo(2048);
395             assertThat(entry.background.bytesReadFromStorage).isEqualTo(1024);
396             assertThat(entry.background.bytesWrittenToStorage).isEqualTo(2048);
397             assertThat(entry.background.fsyncCalls).isEqualTo(1);
398         }
399     }
400 
401     @Test
testUidIoStatEntryMissingFields()402     public void testUidIoStatEntryMissingFields() throws Exception {
403         try (TemporaryFile statsFile = new TemporaryFile(TAG)) {
404             statsFile.write("0 256797495 181736102 362132480 947167232 0 0 0 0 250 0\n"
405                 + "1 2 3 4 5 6 7 8 9\n");
406 
407             ProcfsUidIoStatsProvider statsProvider = new ProcfsUidIoStatsProvider(
408                 statsFile.getPath());
409 
410             SparseArray<UidIoRecord> entries = statsProvider.load();
411 
412             assertThat(entries).isNull();
413         }
414     }
415 
416     @Test
testUidIoStatEntryNonNumericFields()417     public void testUidIoStatEntryNonNumericFields() throws Exception {
418         try (TemporaryFile statsFile = new TemporaryFile(TAG)) {
419             statsFile.write("0 256797495 181736102 362132480 947167232 0 0 0 0 250 0\n"
420                 + "notanumber 489007 196802 0 20480 51474 2048 1024 2048 1 1\n");
421 
422             ProcfsUidIoStatsProvider statsProvider = new ProcfsUidIoStatsProvider(
423                 statsFile.getPath());
424 
425             SparseArray<UidIoRecord> entries = statsProvider.load();
426 
427             assertThat(entries).isNull();
428         }
429     }
430 
431     @Test
432     @SuppressWarnings("TruthSelfEquals")
testUidIoStatEntryEquality()433     public void testUidIoStatEntryEquality() throws Exception {
434         IoStatsEntry statEntry1 = new IoStatsEntry(10, 1234,
435             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
436             new IoStatsEntry.Metrics(100, 200, 300, 400, 500));
437         IoStatsEntry statEntry2 = new IoStatsEntry(10, 1234,
438             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
439             new IoStatsEntry.Metrics(100, 200, 300, 400, 500));
440         IoStatsEntry statEntry3 = new IoStatsEntry(30, 4567,
441             new IoStatsEntry.Metrics(1, 20, 30, 42, 50),
442             new IoStatsEntry.Metrics(10, 200, 300, 420, 500));
443         IoStatsEntry statEntry4 = new IoStatsEntry(11, 6541,
444             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
445             new IoStatsEntry.Metrics(100, 200, 300, 400, 500));
446         IoStatsEntry statEntry5 = new IoStatsEntry(10, 1234,
447             new IoStatsEntry.Metrics(10, 20, 30, 40, 0),
448             new IoStatsEntry.Metrics(100, 200, 300, 400, 500));
449 
450         assertThat(statEntry1).isEqualTo(statEntry1);
451         assertThat(statEntry2).isEqualTo(statEntry1);
452         assertThat(statEntry1).isNotSameInstanceAs(statEntry3);
453         assertThat(statEntry1).isNotSameInstanceAs(statEntry4);
454         assertThat(statEntry1).isNotSameInstanceAs(statEntry5);
455     }
456 
457     @Test
testUidIoStatEntryParcel()458     public void testUidIoStatEntryParcel() throws Exception {
459         IoStatsEntry statEntry = new IoStatsEntry(10, 5000,
460             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
461             new IoStatsEntry.Metrics(100, 200, 300, 400, 500));
462         Parcel p = Parcel.obtain();
463         statEntry.writeToParcel(p, 0);
464         p.setDataPosition(0);
465         IoStatsEntry other = new IoStatsEntry(p);
466         assertThat(statEntry).isEqualTo(other);
467     }
468 
469     @Test
testUidIoStatEntryJson()470     public void testUidIoStatEntryJson() throws Exception {
471         try (TemporaryFile temporaryFile = new TemporaryFile(TAG)) {
472             IoStatsEntry statEntry = new IoStatsEntry(10, 1200,
473                 new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
474                 new IoStatsEntry.Metrics(100, 200, 300, 400, 500));
475             try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(temporaryFile.getFile()))) {
476                 statEntry.writeToJson(jsonWriter);
477             }
478             JSONObject jsonObject = new JSONObject(
479                 new String(Files.readAllBytes(temporaryFile.getPath())));
480             IoStatsEntry other = new IoStatsEntry(jsonObject);
481             assertThat(other).isEqualTo(statEntry);
482         }
483     }
484 
485 
486     @Test
testUidIoStatEntryDelta()487     public void testUidIoStatEntryDelta() throws Exception {
488         IoStatsEntry statEntry1 = new IoStatsEntry(10, 1000,
489             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
490             new IoStatsEntry.Metrics(60, 70, 80, 90, 100));
491 
492         IoStatsEntry statEntry2 = new IoStatsEntry(10,2000,
493             new IoStatsEntry.Metrics(110, 120, 130, 140, 150),
494             new IoStatsEntry.Metrics(260, 370, 480, 500, 110));
495 
496         IoStatsEntry statEntry3 = new IoStatsEntry(30, 3000,
497             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
498             new IoStatsEntry.Metrics(100, 200, 300, 400, 500));
499 
500 
501         IoStatsEntry delta21 = statEntry2.delta(statEntry1);
502         assertThat(delta21).isNotNull();
503         assertThat(delta21.uid).isEqualTo(statEntry1.uid);
504 
505         assertThat(delta21.runtimeMillis).isEqualTo(1000);
506         assertThat(delta21.foreground.bytesRead).isEqualTo(100);
507         assertThat(delta21.foreground.bytesWritten).isEqualTo(100);
508         assertThat(delta21.foreground.bytesReadFromStorage).isEqualTo(100);
509         assertThat(delta21.foreground.bytesWrittenToStorage).isEqualTo(100);
510         assertThat(delta21.foreground.fsyncCalls).isEqualTo(100);
511 
512         assertThat(delta21.background.bytesRead).isEqualTo(200);
513         assertThat(delta21.background.bytesWritten).isEqualTo(300);
514         assertThat(delta21.background.bytesReadFromStorage).isEqualTo(400);
515         assertThat(delta21.background.bytesWrittenToStorage).isEqualTo(410);
516         assertThat(delta21.background.fsyncCalls).isEqualTo(10);
517 
518         try {
519             statEntry3.delta(statEntry1);
520             fail("delta only allowed on stats for matching user ID");
521         } catch (IllegalArgumentException e) {
522             // test passed
523         }
524     }
525 
526     @Test
testUidIoStatsRecordDelta()527     public void testUidIoStatsRecordDelta() throws Exception {
528         IoStatsEntry statEntry = new IoStatsEntry(10, 1000,
529             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
530             new IoStatsEntry.Metrics(60, 70, 80, 90, 100));
531 
532         UidIoRecord statRecord = new UidIoRecord(10,
533             20, 20, 30, 50, 70,
534             80, 70, 80, 100, 110);
535 
536         UidIoRecord delta = statRecord.delta(statEntry);
537 
538         assertThat(delta).isNotNull();
539         assertThat(delta.uid).isEqualTo(statRecord.uid);
540 
541         assertThat(delta.foreground_rchar).isEqualTo(10);
542         assertThat(delta.foreground_wchar).isEqualTo(0);
543         assertThat(delta.foreground_read_bytes).isEqualTo(0);
544         assertThat(delta.foreground_write_bytes).isEqualTo(10);
545         assertThat(delta.foreground_fsync).isEqualTo(20);
546 
547         assertThat(delta.background_rchar).isEqualTo(20);
548         assertThat(delta.background_wchar).isEqualTo(0);
549         assertThat(delta.background_read_bytes).isEqualTo(0);
550         assertThat(delta.background_write_bytes).isEqualTo(10);
551         assertThat(delta.background_fsync).isEqualTo(10);
552 
553         statRecord = new UidIoRecord(30,
554             20, 20, 30, 50, 70,
555             80, 70, 80, 100, 110);
556 
557         try {
558             statRecord.delta(statEntry);
559             fail("delta only allowed on records for matching user ID");
560         } catch (IllegalArgumentException e) {
561             // test passed
562         }
563     }
564 
565     @Test
566     @SuppressWarnings("TruthSelfEquals")
testUidIoStatsDelta()567     public void testUidIoStatsDelta() throws Exception {
568         IoStatsEntry entry10 = new IoStatsEntry(10, 1000,
569             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
570             new IoStatsEntry.Metrics(60, 70, 80, 90, 100));
571 
572         IoStatsEntry entry20 = new IoStatsEntry(20, 2000,
573             new IoStatsEntry.Metrics(200, 60, 100, 30, 40),
574             new IoStatsEntry.Metrics(20, 10, 20, 0, 0));
575 
576         IoStatsEntry entry30 = new IoStatsEntry(30, 2000,
577             new IoStatsEntry.Metrics(50, 100, 100, 30, 40),
578             new IoStatsEntry.Metrics(30, 0, 0, 0, 0));
579 
580         List<IoStatsEntry> statsEntries1 = List.of(entry10, entry20);
581         List<IoStatsEntry> statsEntries2 = List.of(entry20, entry30);
582 
583         IoStats delta1 = new IoStats(statsEntries1, 5000);
584         IoStats delta2 = new IoStats(statsEntries1, 5000);
585         IoStats delta3 = new IoStats(statsEntries2, 3000);
586         IoStats delta4 = new IoStats(statsEntries1, 5000);
587 
588         assertThat(delta1).isEqualTo(delta1);
589         assertThat(delta2).isEqualTo(delta1);
590         assertThat(delta1).isNotSameInstanceAs(delta3);
591         assertThat(delta3).isNotSameInstanceAs(delta4);
592     }
593 
594     @Test
testUidIoStatsTotals()595     public void testUidIoStatsTotals() throws Exception {
596         IoStatsEntry entry10 = new IoStatsEntry(10, 1000,
597             new IoStatsEntry.Metrics(20, 0, 10, 0, 0),
598             new IoStatsEntry.Metrics(10, 50, 0, 20, 2));
599 
600         IoStatsEntry entry20 = new IoStatsEntry(20, 1000,
601             new IoStatsEntry.Metrics(100, 200, 50, 200, 1),
602             new IoStatsEntry.Metrics(0, 30, 10, 0, 1));
603 
604         List<IoStatsEntry> statsEntries = List.of(entry10, entry20);
605 
606         IoStats delta = new IoStats(statsEntries, 5000);
607 
608         IoStatsEntry.Metrics foregroundTotals = delta.getForegroundTotals();
609         IoStatsEntry.Metrics backgroundTotals = delta.getBackgroundTotals();
610         IoStatsEntry.Metrics overallTotals = delta.getTotals();
611 
612         assertThat(foregroundTotals.bytesRead).isEqualTo(120);
613         assertThat(foregroundTotals.bytesWritten).isEqualTo(200);
614         assertThat(foregroundTotals.bytesReadFromStorage).isEqualTo(60);
615         assertThat(foregroundTotals.bytesWrittenToStorage).isEqualTo(200);
616         assertThat(foregroundTotals.fsyncCalls).isEqualTo(1);
617 
618 
619         assertThat(backgroundTotals.bytesRead).isEqualTo(10);
620         assertThat(backgroundTotals.bytesWritten).isEqualTo(80);
621         assertThat(backgroundTotals.bytesReadFromStorage).isEqualTo(10);
622         assertThat(backgroundTotals.bytesWrittenToStorage).isEqualTo(20);
623         assertThat(backgroundTotals.fsyncCalls).isEqualTo(3);
624 
625         assertThat(overallTotals.bytesRead).isEqualTo(130);
626         assertThat(overallTotals.bytesWritten).isEqualTo(280);
627         assertThat(overallTotals.bytesReadFromStorage).isEqualTo(70);
628         assertThat(overallTotals.bytesWrittenToStorage).isEqualTo(220);
629         assertThat(overallTotals.fsyncCalls).isEqualTo(4);
630     }
631 
632     @Test
testUidIoStatsDeltaParcel()633     public void testUidIoStatsDeltaParcel() throws Exception {
634         IoStatsEntry entry10 = new IoStatsEntry(10, 1000,
635             new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
636             new IoStatsEntry.Metrics(60, 70, 80, 90, 100));
637 
638         IoStatsEntry entry20 = new IoStatsEntry(20, 2000,
639             new IoStatsEntry.Metrics(200, 60, 100, 30, 40),
640             new IoStatsEntry.Metrics(20, 10, 20, 0, 0));
641 
642         List<IoStatsEntry> statsEntries = List.of(entry10, entry20);
643 
644         IoStats statsDelta = new IoStats(statsEntries, 5000);
645 
646         Parcel p = Parcel.obtain();
647         statsDelta.writeToParcel(p, 0);
648         p.setDataPosition(0);
649 
650         IoStats parceledStatsDelta = new IoStats(p);
651 
652         assertThat(parceledStatsDelta.getTimestamp()).isEqualTo(statsDelta.getTimestamp());
653 
654         assertEquals(2, parceledStatsDelta.getStats().stream()
655                 .filter(e -> e.equals(entry10) || e.equals(entry20))
656                 .count());
657     }
658 
659     @Test
testUidIoStatsDeltaJson()660     public void testUidIoStatsDeltaJson() throws Exception {
661         try (TemporaryFile temporaryFile = new TemporaryFile(TAG)) {
662             IoStatsEntry entry10 = new IoStatsEntry(10, 1000,
663                 new IoStatsEntry.Metrics(10, 20, 30, 40, 50),
664                 new IoStatsEntry.Metrics(60, 70, 80, 90, 100));
665 
666             IoStatsEntry entry20 = new IoStatsEntry(20, 2000,
667                 new IoStatsEntry.Metrics(200, 60, 100, 30, 40),
668                 new IoStatsEntry.Metrics(20, 10, 20, 0, 0));
669 
670             List<IoStatsEntry> statsEntries = List.of(entry10, entry20);
671 
672             IoStats statsDelta = new IoStats(statsEntries, 5000);
673             try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(temporaryFile.getFile()))) {
674                 statsDelta.writeToJson(jsonWriter);
675             }
676             JSONObject jsonObject = new JSONObject(
677                 new String(Files.readAllBytes(temporaryFile.getPath())));
678             IoStats other = new IoStats(jsonObject);
679             assertThat(other).isEqualTo(statsDelta);
680         }
681     }
682 
683     @Test
testLifetimeWriteInfo()684     public void testLifetimeWriteInfo() throws Exception {
685         try (TemporaryDirectory temporaryDirectory = new TemporaryDirectory(TAG)) {
686             try (TemporaryDirectory ext4 = temporaryDirectory.getSubdirectory("ext4");
687                  TemporaryDirectory f2fs = temporaryDirectory.getSubdirectory("f2fs")) {
688                 try(TemporaryDirectory ext4_part1 = ext4.getSubdirectory("part1");
689                     TemporaryDirectory f2fs_part1 = f2fs.getSubdirectory("part1");
690                     TemporaryDirectory ext4_part2 = ext4.getSubdirectory("part2");
691                     TemporaryDirectory f2f2_notpart = f2fs.getSubdirectory("nopart")) {
692                     Files.write(ext4_part1.getPath().resolve("lifetime_write_kbytes"),
693                         Collections.singleton("123"));
694                     Files.write(f2fs_part1.getPath().resolve("lifetime_write_kbytes"),
695                         Collections.singleton("250"));
696                     Files.write(ext4_part2.getPath().resolve("lifetime_write_kbytes"),
697                         Collections.singleton("2147483660"));
698 
699                     LifetimeWriteInfo expected_ext4_part1 =
700                         new LifetimeWriteInfo("part1", "ext4", 123*1024);
701                     LifetimeWriteInfo expected_f2fs_part1 =
702                         new LifetimeWriteInfo("part1", "f2fs", 250*1024);
703                     LifetimeWriteInfo expected_ext4_part2 =
704                         new LifetimeWriteInfo("part2", "ext4", 2147483660L*1024);
705 
706                     SysfsLifetimeWriteInfoProvider sysfsLifetimeWriteInfoProvider =
707                         new SysfsLifetimeWriteInfoProvider(temporaryDirectory.getDirectory());
708 
709                     LifetimeWriteInfo[] writeInfos = sysfsLifetimeWriteInfoProvider.load();
710 
711                     assertThat(writeInfos).isNotNull();
712                     assertThat(writeInfos.length).isEqualTo(3);
713                     assertTrue(Arrays.stream(writeInfos).anyMatch(
714                             li -> li.equals(expected_ext4_part1)));
715                     assertTrue(Arrays.stream(writeInfos).anyMatch(
716                             li -> li.equals(expected_ext4_part2)));
717                     assertTrue(Arrays.stream(writeInfos).anyMatch(
718                             li -> li.equals(expected_f2fs_part1)));
719                 }
720             }
721         }
722     }
723 
724     @Test
725     @SuppressWarnings("TruthSelfEquals")
testLifetimeWriteInfoEquality()726     public void testLifetimeWriteInfoEquality() throws Exception {
727         LifetimeWriteInfo writeInfo = new LifetimeWriteInfo("part1", "ext4", 123);
728         LifetimeWriteInfo writeInfoEq = new LifetimeWriteInfo("part1", "ext4", 123);
729 
730         LifetimeWriteInfo writeInfoNeq1 = new LifetimeWriteInfo("part2", "ext4", 123);
731         LifetimeWriteInfo writeInfoNeq2 = new LifetimeWriteInfo("part1", "f2fs", 123);
732         LifetimeWriteInfo writeInfoNeq3 = new LifetimeWriteInfo("part1", "ext4", 100);
733 
734         assertThat(writeInfo).isEqualTo(writeInfo);
735         assertThat(writeInfoEq).isEqualTo(writeInfo);
736         assertThat(writeInfo).isNotSameInstanceAs(writeInfoNeq1);
737         assertThat(writeInfo).isNotSameInstanceAs(writeInfoNeq2);
738         assertThat(writeInfo).isNotSameInstanceAs(writeInfoNeq3);
739     }
740 
741     @Test
testLifetimeWriteInfoParcel()742     public void testLifetimeWriteInfoParcel() throws Exception {
743         LifetimeWriteInfo lifetimeWriteInfo = new LifetimeWriteInfo("part1", "ext4", 1024);
744 
745         Parcel p = Parcel.obtain();
746         lifetimeWriteInfo.writeToParcel(p, 0);
747         p.setDataPosition(0);
748 
749         LifetimeWriteInfo parceled = new LifetimeWriteInfo(p);
750 
751         assertThat(parceled).isEqualTo(lifetimeWriteInfo);
752     }
753 
754     @Test
testLifetimeWriteInfoJson()755     public void testLifetimeWriteInfoJson() throws Exception {
756         try (TemporaryFile temporaryFile = new TemporaryFile(TAG)) {
757             LifetimeWriteInfo lifetimeWriteInfo = new LifetimeWriteInfo("part1", "ext4", 1024);
758 
759             try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(temporaryFile.getFile()))) {
760                 lifetimeWriteInfo.writeToJson(jsonWriter);
761             }
762             JSONObject jsonObject = new JSONObject(
763                 new String(Files.readAllBytes(temporaryFile.getPath())));
764             LifetimeWriteInfo other = new LifetimeWriteInfo(jsonObject);
765             assertThat(other).isEqualTo(lifetimeWriteInfo);
766         }
767     }
768 }
769