1 /* 2 * Copyright (C) 2021 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.server.apphibernation; 18 19 import static org.junit.Assert.assertEquals; 20 21 import android.os.FileUtils; 22 import android.platform.test.annotations.Presubmit; 23 import android.util.proto.ProtoInputStream; 24 import android.util.proto.ProtoOutputStream; 25 26 import androidx.annotation.NonNull; 27 import androidx.annotation.Nullable; 28 import androidx.test.InstrumentationRegistry; 29 import androidx.test.filters.SmallTest; 30 31 import org.junit.After; 32 import org.junit.Before; 33 import org.junit.Test; 34 import org.mockito.Mockito; 35 36 import java.io.File; 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collection; 41 import java.util.List; 42 import java.util.concurrent.Callable; 43 import java.util.concurrent.ExecutionException; 44 import java.util.concurrent.Future; 45 import java.util.concurrent.ScheduledExecutorService; 46 import java.util.concurrent.ScheduledFuture; 47 import java.util.concurrent.TimeUnit; 48 import java.util.concurrent.TimeoutException; 49 50 51 @SmallTest 52 @Presubmit 53 public class HibernationStateDiskStoreTest { 54 private static final String STATES_FILE_NAME = "states"; 55 private final MockScheduledExecutorService mMockScheduledExecutorService = 56 new MockScheduledExecutorService(); 57 58 private File mFile; 59 private HibernationStateDiskStore<String> mHibernationStateDiskStore; 60 61 62 @Before setUp()63 public void setUp() { 64 mFile = new File(InstrumentationRegistry.getContext().getCacheDir(), "test"); 65 mHibernationStateDiskStore = new HibernationStateDiskStore<>(mFile, 66 new MockProtoReadWriter(), mMockScheduledExecutorService, STATES_FILE_NAME); 67 } 68 69 @After tearDown()70 public void tearDown() { 71 FileUtils.deleteContentsAndDir(mFile); 72 } 73 74 @Test testScheduleWriteHibernationStates_writesDataThatCanBeRead()75 public void testScheduleWriteHibernationStates_writesDataThatCanBeRead() { 76 // GIVEN some data to be written 77 List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B")); 78 79 // WHEN the data is written 80 mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite); 81 mMockScheduledExecutorService.executeScheduledTask(); 82 83 // THEN the read data is equal to what was written 84 List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates(); 85 for (int i = 0; i < toWrite.size(); i++) { 86 assertEquals(toWrite.get(i), storedStrings.get(i)); 87 } 88 } 89 90 @Test testScheduleWriteHibernationStates_laterWritesOverwritePrevious()91 public void testScheduleWriteHibernationStates_laterWritesOverwritePrevious() { 92 // GIVEN store has some data it is scheduled to write 93 mHibernationStateDiskStore.scheduleWriteHibernationStates( 94 new ArrayList<>(Arrays.asList("C", "D"))); 95 96 // WHEN a write is scheduled with new data 97 List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B")); 98 mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite); 99 mMockScheduledExecutorService.executeScheduledTask(); 100 101 // THEN the written data is the last scheduled data 102 List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates(); 103 for (int i = 0; i < toWrite.size(); i++) { 104 assertEquals(toWrite.get(i), storedStrings.get(i)); 105 } 106 } 107 108 /** 109 * Mock proto read / writer that just writes and reads a list of String data. 110 */ 111 private final class MockProtoReadWriter implements ProtoReadWriter<List<String>> { 112 private static final long FIELD_ID = 1; 113 114 @Override writeToProto(@onNull ProtoOutputStream stream, @NonNull List<String> data)115 public void writeToProto(@NonNull ProtoOutputStream stream, 116 @NonNull List<String> data) { 117 for (int i = 0, size = data.size(); i < size; i++) { 118 stream.write(FIELD_ID, data.get(i)); 119 } 120 } 121 122 @Nullable 123 @Override readFromProto(@onNull ProtoInputStream stream)124 public List<String> readFromProto(@NonNull ProtoInputStream stream) 125 throws IOException { 126 ArrayList<String> list = new ArrayList<>(); 127 while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { 128 list.add(stream.readString(FIELD_ID)); 129 } 130 return list; 131 } 132 } 133 134 /** 135 * Mock scheduled executor service that has minimum implementation and can synchronously 136 * execute scheduled tasks. 137 */ 138 private final class MockScheduledExecutorService implements ScheduledExecutorService { 139 140 Runnable mScheduledRunnable = null; 141 142 @Override schedule(Runnable command, long delay, TimeUnit unit)143 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { 144 mScheduledRunnable = command; 145 return Mockito.mock(ScheduledFuture.class); 146 } 147 148 @Override schedule(Callable<V> callable, long delay, TimeUnit unit)149 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { 150 throw new UnsupportedOperationException(); 151 } 152 153 @Override scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)154 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, 155 long period, TimeUnit unit) { 156 throw new UnsupportedOperationException(); 157 } 158 159 @Override scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)160 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, 161 long delay, TimeUnit unit) { 162 throw new UnsupportedOperationException(); 163 } 164 165 @Override shutdown()166 public void shutdown() { 167 throw new UnsupportedOperationException(); 168 } 169 170 @Override shutdownNow()171 public List<Runnable> shutdownNow() { 172 throw new UnsupportedOperationException(); 173 } 174 175 @Override isShutdown()176 public boolean isShutdown() { 177 return false; 178 } 179 180 @Override isTerminated()181 public boolean isTerminated() { 182 return false; 183 } 184 185 @Override awaitTermination(long timeout, TimeUnit unit)186 public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { 187 throw new UnsupportedOperationException(); 188 } 189 190 @Override submit(Callable<T> task)191 public <T> Future<T> submit(Callable<T> task) { 192 throw new UnsupportedOperationException(); 193 } 194 195 @Override submit(Runnable task, T result)196 public <T> Future<T> submit(Runnable task, T result) { 197 throw new UnsupportedOperationException(); 198 } 199 200 @Override submit(Runnable task)201 public Future<?> submit(Runnable task) { 202 throw new UnsupportedOperationException(); 203 } 204 205 @Override invokeAll(Collection<? extends Callable<T>> tasks)206 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) 207 throws InterruptedException { 208 throw new UnsupportedOperationException(); 209 } 210 211 @Override invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)212 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, 213 TimeUnit unit) throws InterruptedException { 214 throw new UnsupportedOperationException(); 215 } 216 217 @Override invokeAny(Collection<? extends Callable<T>> tasks)218 public <T> T invokeAny(Collection<? extends Callable<T>> tasks) 219 throws InterruptedException, ExecutionException { 220 throw new UnsupportedOperationException(); 221 } 222 223 @Override invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)224 public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) 225 throws InterruptedException, ExecutionException, TimeoutException { 226 throw new UnsupportedOperationException(); 227 } 228 229 @Override execute(Runnable command)230 public void execute(Runnable command) { 231 throw new UnsupportedOperationException(); 232 } 233 executeScheduledTask()234 void executeScheduledTask() { 235 mScheduledRunnable.run(); 236 } 237 } 238 } 239