1 /* <lambda>null2 * Copyright (C) 2024 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.systemui.communal.data.backup 18 19 import androidx.room.Room 20 import androidx.test.ext.junit.runners.AndroidJUnit4 21 import androidx.test.filters.SmallTest 22 import com.android.systemui.SysuiTestCase 23 import com.android.systemui.communal.data.db.CommunalDatabase 24 import com.android.systemui.communal.data.db.CommunalWidgetDao 25 import com.android.systemui.communal.nano.CommunalHubState 26 import com.android.systemui.communal.shared.model.SpanValue 27 import com.android.systemui.communal.shared.model.toFixed 28 import com.android.systemui.communal.shared.model.toResponsive 29 import com.android.systemui.lifecycle.InstantTaskExecutorRule 30 import com.google.common.truth.Correspondence 31 import com.google.common.truth.Truth.assertThat 32 import java.io.FileNotFoundException 33 import java.nio.charset.Charset 34 import org.junit.After 35 import org.junit.Before 36 import org.junit.Rule 37 import org.junit.Test 38 import org.junit.runner.RunWith 39 40 @SmallTest 41 @RunWith(AndroidJUnit4::class) 42 class CommunalBackupUtilsTest : SysuiTestCase() { 43 @JvmField @Rule val instantTaskExecutor = InstantTaskExecutorRule() 44 45 private lateinit var database: CommunalDatabase 46 private lateinit var dao: CommunalWidgetDao 47 private lateinit var underTest: CommunalBackupUtils 48 49 @Before 50 fun setup() { 51 database = 52 Room.inMemoryDatabaseBuilder(context, CommunalDatabase::class.java) 53 .allowMainThreadQueries() 54 .build() 55 CommunalDatabase.setInstance(database) 56 57 dao = database.communalWidgetDao() 58 underTest = CommunalBackupUtils(context) 59 } 60 61 @After 62 fun teardown() { 63 database.close() 64 underTest.clear() 65 } 66 67 @Test 68 fun getCommunalHubState_returnsExpectedWidgets() { 69 // Set up database 70 val expectedWidgets = 71 listOf( 72 FakeWidgetMetadata( 73 widgetId = 11, 74 componentName = "com.android.fakePackage1/fakeWidget1", 75 rank = 0, 76 userSerialNumber = 0, 77 spanY = SpanValue.Responsive(1), 78 ), 79 FakeWidgetMetadata( 80 widgetId = 12, 81 componentName = "com.android.fakePackage2/fakeWidget2", 82 rank = 1, 83 userSerialNumber = 0, 84 spanY = SpanValue.Responsive(2), 85 ), 86 FakeWidgetMetadata( 87 widgetId = 13, 88 componentName = "com.android.fakePackage3/fakeWidget3", 89 rank = 2, 90 userSerialNumber = 10, 91 spanY = SpanValue.Responsive(3), 92 ), 93 ) 94 expectedWidgets.forEach { 95 dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber, it.spanY) 96 } 97 98 // Get communal hub state 99 val state = underTest.getCommunalHubState() 100 val actualWidgets = state.widgets.toList() 101 102 // Verify the state contains widgets as expected 103 assertThat(actualWidgets) 104 .comparingElementsUsing(represents) 105 .containsExactlyElementsIn(expectedWidgets) 106 } 107 108 @Test 109 fun write_existingContentIsOverwritten() { 110 // Write old data 111 val dataToWrite = "I am old data. Erase me." 112 underTest.writeBytesToDisk(dataToWrite.toByteArray(Charset.defaultCharset())) 113 114 // Verify old data written 115 var dataRead = underTest.readBytesFromDisk().toString(Charset.defaultCharset()) 116 assertThat(dataRead).isEqualTo(dataToWrite) 117 118 // Write new data 119 val newDataToWrite = "I am new data." 120 underTest.writeBytesToDisk(newDataToWrite.toByteArray(Charset.defaultCharset())) 121 122 // Verify new data overwrites old 123 dataRead = underTest.readBytesFromDisk().toString(Charset.defaultCharset()) 124 assertThat(dataRead).isEqualTo(newDataToWrite) 125 } 126 127 @Test(expected = FileNotFoundException::class) 128 fun clear_returnsTrueWhenFileDeleted() { 129 // Write bytes to disk 130 underTest.writeBytesToDisk(byteArrayOf(1, 2, 3)) 131 132 assertThat(underTest.clear()).isTrue() 133 134 // Verify a read after that throws a FileNotFoundException 135 underTest.readBytesFromDisk() 136 } 137 138 @Test 139 fun clear_returnsFalseWhenFileDoesNotExist() { 140 assertThat(underTest.clear()).isFalse() 141 } 142 143 @Test 144 fun fileExists() { 145 assertThat(underTest.fileExists()).isFalse() 146 147 underTest.writeBytesToDisk(byteArrayOf(1, 2, 3)) 148 assertThat(underTest.fileExists()).isTrue() 149 150 underTest.clear() 151 assertThat(underTest.fileExists()).isFalse() 152 } 153 154 data class FakeWidgetMetadata( 155 val widgetId: Int, 156 val componentName: String, 157 val rank: Int, 158 val userSerialNumber: Int, 159 val spanY: SpanValue, 160 ) 161 162 companion object { 163 /** 164 * A comparator for whether a [CommunalHubState.CommunalWidgetItem] represents a 165 * [FakeWidgetMetadata] 166 */ 167 val represents: Correspondence<CommunalHubState.CommunalWidgetItem, FakeWidgetMetadata> = 168 Correspondence.from( 169 { actual, expected -> 170 actual?.widgetId == expected?.widgetId && 171 actual?.componentName == expected?.componentName && 172 actual?.rank == expected?.rank && 173 actual?.userSerialNumber == expected?.userSerialNumber && 174 actual?.spanY == expected?.spanY?.toFixed()?.value && 175 actual?.spanYNew == expected?.spanY?.toResponsive()?.value 176 }, 177 "represents", 178 ) 179 } 180 } 181