1 /*
2  * Copyright (C) 2020 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.eventlib;
18 
19 import static com.android.eventlib.truth.EventLogsSubject.assertThat;
20 
21 import static java.time.temporal.ChronoUnit.SECONDS;
22 
23 import android.util.Log;
24 
25 import com.android.bedstead.nene.TestApis;
26 import com.android.bedstead.nene.exceptions.NeneException;
27 import com.android.bedstead.nene.exceptions.PollValueFailedException;
28 
29 import com.google.errorprone.annotations.CanIgnoreReturnValue;
30 
31 import java.io.Serializable;
32 import java.time.Duration;
33 import java.time.Instant;
34 
35 /** Interface to interact with the results of an {@link EventLogsQuery}. */
36 public abstract class EventLogs<E extends Event> implements Serializable {
37 
38     private static final long serialVersionUID = 1;
39 
40     static final Duration DEFAULT_POLL_TIMEOUT = Duration.ofMinutes(5);
41 
42     // We need to set this earlier than construction otherwise we will skip all events that happen
43     // before creating the first query
44     static Instant sEarliestLogTime = Instant.now().minus(30, SECONDS);
45 
46     /**
47      * Returns the {@link EventQuerier} to be used to interact with the
48      * appropriate {@link Event} store.
49      */
getQuerier()50     protected abstract EventQuerier<E> getQuerier();
51 
52     /**
53      * Ensures that future calls to {@link #get()}, {@link #next()}, and {@link #poll()} only return
54      * events which are not already logged before this call to {@link #resetLogs()}.
55      */
resetLogs()56     public static void resetLogs() {
57         // We delay 1 ms before and after to separate the cutoff from logs which are
58         // triggered immediately by the tests - this makes behaviour more predictable
59 
60         try {
61             Thread.sleep(1);
62         } catch (InterruptedException e) {
63             Log.d("EventLogs", "Interrupted when sleeping during resetLogs");
64         }
65 
66         sEarliestLogTime = Instant.now();
67 
68         try {
69             Thread.sleep(1);
70         } catch (InterruptedException e) {
71             Log.d("EventLogs", "Interrupted when sleeping during resetLogs");
72         }
73     }
74 
75     /**
76      * Gets the earliest logged event matching the query which has not be returned by a previous
77      * call to {@link #poll()}, or blocks until a matching event is logged.
78      *
79      * <p>This will timeout after {@code timeout} and return null if no matching event is logged.
80      */
poll(Duration timeout)81     public E poll(Duration timeout) {
82         return getQuerier().poll(sEarliestLogTime, timeout);
83     }
84 
85     /**
86      * Gets the earliest logged event matching the query which has not be returned by a previous
87      * call to {@link #poll()}, or blocks until a matching event is logged.
88      *
89      * <p>This will timeout after {@link #DEFAULT_POLL_TIMEOUT} and return null if no matching
90      * event is logged.
91      */
poll()92     public E poll() {
93         return poll(DEFAULT_POLL_TIMEOUT);
94     }
95 
96     /**
97      * Returns immediately if there is an existing event matching the query which has not been
98      * returned by a previous call to {@link #poll()}, or blocks until a matching event is logged.
99      *
100      * <p>This will timeout after {@code timeout} and throw an {@link AssertionError} if no
101      * matching event is logged.
102      */
waitForEvent(Duration timeout)103     public E waitForEvent(Duration timeout) {
104         try {
105             return assertThat(this).eventOccurredWithin(timeout);
106         } catch (PollValueFailedException e) {
107             if (e.getMessage().contains("No existing activity")) {
108                 String activityName = e.getMessage().split(
109                         "named ", 2)[1].split(". ", 2)[0];
110                 String packageName = e.getMessage().split("package ", 2)[1]
111                         .split(". ", 2)[0];
112 
113                 throw new NeneException("Error finding launched activity " + activityName
114                         + " in test app " + packageName
115                         + " (this could mean it didn't launch or the"
116                         + " package crashed). Relevant logs: " + TestApis.logcat().dump(
117                                 l -> l.contains(activityName) || l.contains(packageName)), e);
118             }
119             throw e;
120         }
121     }
122 
123     /**
124      * Returns immediately if there is an existing event matching the query which has not be
125      * returned by a previous call to {@link #poll()}, or blocks until a matching event is logged.
126      *
127      * <p>This will timeout after {@link #DEFAULT_POLL_TIMEOUT} and throw an {@link AssertionError}
128      * if no matching event is logged.
129      */
130     @CanIgnoreReturnValue
waitForEvent()131     public E waitForEvent() {
132         return waitForEvent(DEFAULT_POLL_TIMEOUT);
133     }
134 }
135