1 /* 2 * Copyright (C) 2019 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 android.server.wm; 18 19 import android.os.Build; 20 21 import androidx.annotation.NonNull; 22 import androidx.annotation.Nullable; 23 24 import java.util.ArrayList; 25 import java.util.LinkedList; 26 import java.util.function.Consumer; 27 28 /** A helper utility to track or manage object after a test method is done. */ 29 public class ObjectTracker { 30 private static final boolean DEBUG = "eng".equals(Build.TYPE); 31 private LinkedList<AutoCloseable> mAutoCloseables; 32 private LinkedList<ConsumableEntry> mConsumables; 33 34 /** The interface used for tracking whether an object is consumed. */ 35 public interface Consumable { isConsumed()36 boolean isConsumed(); 37 } 38 39 private static class ConsumableEntry { 40 @NonNull 41 final Consumable mConsumable; 42 @Nullable 43 final Throwable mStackTrace; 44 ConsumableEntry(Consumable consumable, Throwable stackTrace)45 ConsumableEntry(Consumable consumable, Throwable stackTrace) { 46 mConsumable = consumable; 47 mStackTrace = stackTrace; 48 } 49 } 50 51 /** 52 * If a {@link AutoCloseable} should be closed at the end of test, or it is not important when 53 * to close, then we can use this method to manage the {@link AutoCloseable}. Then the extra 54 * indents of try-with-resource can be eliminated. If the caller want to close the object 55 * manually, it should use {@link ObjectTracker#close} to cancel the management. 56 */ manage(@onNull T autoCloseable)57 public <T extends AutoCloseable> T manage(@NonNull T autoCloseable) { 58 if (mAutoCloseables == null) { 59 mAutoCloseables = new LinkedList<>(); 60 } 61 mAutoCloseables.add(autoCloseable); 62 return autoCloseable; 63 } 64 65 /** 66 * Closes the {@link AutoCloseable} and remove from the managed list so it won't be closed twice 67 * when leaving a test method. 68 */ close(@onNull AutoCloseable autoCloseable)69 public void close(@NonNull AutoCloseable autoCloseable) { 70 if (mAutoCloseables == null) { 71 return; 72 } 73 mAutoCloseables.remove(autoCloseable); 74 try { 75 autoCloseable.close(); 76 } catch (Throwable e) { 77 throw new AssertionError("Failed to close " + autoCloseable, e); 78 } 79 } 80 81 /** Tracks the {@link Consumable} to avoid misusing of the object that should do something. */ track(@onNull Consumable consumable)82 public void track(@NonNull Consumable consumable) { 83 if (mConsumables == null) { 84 mConsumables = new LinkedList<>(); 85 } 86 mConsumables.add(new ConsumableEntry(consumable, 87 DEBUG ? new Throwable().fillInStackTrace() : null)); 88 } 89 90 /** 91 * Cleans up the managed object and make sure all tracked {@link Consumable} are consumed. 92 * This method must be called after each test method. 93 */ tearDown(@onNull Consumer<Throwable> errorConsumer)94 public void tearDown(@NonNull Consumer<Throwable> errorConsumer) { 95 ArrayList<Throwable> errors = null; 96 if (mAutoCloseables != null) { 97 while (!mAutoCloseables.isEmpty()) { 98 final AutoCloseable autoCloseable = mAutoCloseables.removeLast(); 99 try { 100 autoCloseable.close(); 101 } catch (Throwable t) { 102 StateLogger.logE("Failed to close " + autoCloseable, t); 103 if (errors == null) { 104 errors = new ArrayList<>(); 105 } 106 errors.add(t); 107 } 108 } 109 } 110 111 if (mConsumables != null) { 112 while (!mConsumables.isEmpty()) { 113 final ConsumableEntry entry = mConsumables.removeFirst(); 114 if (!entry.mConsumable.isConsumed()) { 115 StateLogger.logE("Found unconsumed object " + entry.mConsumable 116 + " that was created from:", entry.mStackTrace); 117 final Throwable t = 118 new IllegalStateException("Unconsumed object " + entry.mConsumable); 119 if (entry.mStackTrace != null) { 120 t.initCause(entry.mStackTrace); 121 } 122 if (errors == null) { 123 errors = new ArrayList<>(); 124 } 125 errors.add(t); 126 } 127 } 128 } 129 130 if (errors != null) { 131 errors.forEach(errorConsumer); 132 } 133 } 134 } 135