blob: af4a374bffddf0bee8f37305651384913049eae8 [file] [log] [blame]
Hugo Benichi5e055182016-06-01 08:50:38 +09001/*
2 * Copyright (C) 2016, 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
17package com.android.server.connectivity;
18
19import android.net.ConnectivityManager.NetworkCallback;
20import android.net.ConnectivityManager;
21import android.net.Network;
22import android.net.metrics.DnsEvent;
Michal Karpinskia2d58602016-09-21 18:45:59 +090023import android.net.metrics.INetdEventListener;
Hugo Benichi5e055182016-06-01 08:50:38 +090024import android.net.metrics.IpConnectivityLog;
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +010025import android.os.RemoteException;
Hugo Benichi5e055182016-06-01 08:50:38 +090026
27import junit.framework.TestCase;
28import org.junit.Before;
29import org.junit.Test;
30import static org.junit.Assert.assertArrayEquals;
31import static org.junit.Assert.assertTrue;
32
33import org.mockito.ArgumentCaptor;
34import org.mockito.Mock;
35import org.mockito.Mockito;
36import org.mockito.MockitoAnnotations;
37import static org.mockito.Mockito.any;
38import static org.mockito.Mockito.anyInt;
39import static org.mockito.Mockito.eq;
40import static org.mockito.Mockito.timeout;
41import static org.mockito.Mockito.times;
42import static org.mockito.Mockito.verify;
43
44import java.io.FileOutputStream;
45import java.io.PrintWriter;
46import java.util.Arrays;
47import java.util.List;
48import java.util.OptionalInt;
49import java.util.stream.IntStream;
50
Michal Karpinskia2d58602016-09-21 18:45:59 +090051public class NetdEventListenerServiceTest extends TestCase {
Hugo Benichi5e055182016-06-01 08:50:38 +090052
Michal Karpinskia2d58602016-09-21 18:45:59 +090053 // TODO: read from NetdEventListenerService after this constant is read from system property
Hugo Benichi5e055182016-06-01 08:50:38 +090054 static final int BATCH_SIZE = 100;
Michal Karpinskia2d58602016-09-21 18:45:59 +090055 static final int EVENT_TYPE = INetdEventListener.EVENT_GETADDRINFO;
56 // TODO: read from INetdEventListener
Hugo Benichi5e055182016-06-01 08:50:38 +090057 static final int RETURN_CODE = 1;
58
59 static final byte[] EVENT_TYPES = new byte[BATCH_SIZE];
60 static final byte[] RETURN_CODES = new byte[BATCH_SIZE];
61 static final int[] LATENCIES = new int[BATCH_SIZE];
62 static {
63 for (int i = 0; i < BATCH_SIZE; i++) {
64 EVENT_TYPES[i] = EVENT_TYPE;
65 RETURN_CODES[i] = RETURN_CODE;
66 LATENCIES[i] = i;
67 }
68 }
69
Michal Karpinski4f261032016-09-26 09:20:25 +010070 NetdEventListenerService mNetdEventListenerService;
Hugo Benichi5e055182016-06-01 08:50:38 +090071
72 @Mock ConnectivityManager mCm;
73 @Mock IpConnectivityLog mLog;
74 ArgumentCaptor<NetworkCallback> mCallbackCaptor;
75 ArgumentCaptor<DnsEvent> mEvCaptor;
76
77 public void setUp() {
78 MockitoAnnotations.initMocks(this);
79 mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
80 mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
Michal Karpinski4f261032016-09-26 09:20:25 +010081 mNetdEventListenerService = new NetdEventListenerService(mCm, mLog);
Hugo Benichi5e055182016-06-01 08:50:38 +090082
83 verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
84 }
85
86 public void testOneBatch() throws Exception {
87 log(105, LATENCIES);
88 log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
89
90 verifyLoggedEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
91
92 log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE));
93
94 mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
95 verifyLoggedEvents(
96 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
97 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES));
98 }
99
100 public void testSeveralBatches() throws Exception {
101 log(105, LATENCIES);
102 log(106, LATENCIES);
103 log(105, LATENCIES);
104 log(107, LATENCIES);
105
106 verifyLoggedEvents(
107 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
108 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
109 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
110 new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
111 }
112
113 public void testBatchAndNetworkLost() throws Exception {
114 byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20);
115 byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20);
116 int[] latencies = Arrays.copyOf(LATENCIES, 20);
117
118 log(105, LATENCIES);
119 log(105, latencies);
120 mCallbackCaptor.getValue().onLost(new Network(105));
121 log(105, LATENCIES);
122
123 verifyLoggedEvents(
124 new DnsEvent(105, eventTypes, returnCodes, latencies),
125 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
126 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
127 }
128
129 public void testConcurrentBatchesAndDumps() throws Exception {
130 final long stop = System.currentTimeMillis() + 100;
131 final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
132 new Thread() {
133 public void run() {
134 while (System.currentTimeMillis() < stop) {
Michal Karpinski4f261032016-09-26 09:20:25 +0100135 mNetdEventListenerService.dump(pw);
Hugo Benichi5e055182016-06-01 08:50:38 +0900136 }
137 }
138 }.start();
139
140 logAsync(105, LATENCIES);
141 logAsync(106, LATENCIES);
142 logAsync(107, LATENCIES);
143
144 verifyLoggedEvents(500,
145 new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
146 new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
147 new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
148 }
149
150 public void testConcurrentBatchesAndNetworkLoss() throws Exception {
151 logAsync(105, LATENCIES);
152 Thread.sleep(10L);
153 // call onLost() asynchronously to logAsync's onDnsEvent() calls.
154 mCallbackCaptor.getValue().onLost(new Network(105));
155
156 // do not verify unpredictable batch
157 verify(mLog, timeout(500).times(1)).log(any());
158 }
159
160 void log(int netId, int[] latencies) {
Michal Karpinskidd9bb4f2016-10-12 14:59:26 +0100161 try {
162 for (int l : latencies) {
163 mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null,
164 0, 0);
165 }
166 } catch (RemoteException re) {
167 throw re.rethrowFromSystemServer();
Hugo Benichi5e055182016-06-01 08:50:38 +0900168 }
169 }
170
171 void logAsync(int netId, int[] latencies) {
172 new Thread() {
173 public void run() {
174 log(netId, latencies);
175 }
176 }.start();
177 }
178
179 void verifyLoggedEvents(DnsEvent... expected) {
180 verifyLoggedEvents(0, expected);
181 }
182
183 void verifyLoggedEvents(int wait, DnsEvent... expectedEvents) {
184 verify(mLog, timeout(wait).times(expectedEvents.length)).log(mEvCaptor.capture());
185 for (DnsEvent got : mEvCaptor.getAllValues()) {
186 OptionalInt index = IntStream.range(0, expectedEvents.length)
187 .filter(i -> eventsEqual(expectedEvents[i], got))
188 .findFirst();
189 // Don't match same expected event more than once.
190 index.ifPresent(i -> expectedEvents[i] = null);
191 assertTrue(index.isPresent());
192 }
193 }
194
195 /** equality function for DnsEvent to avoid overriding equals() and hashCode(). */
196 static boolean eventsEqual(DnsEvent expected, DnsEvent got) {
197 return (expected == got) || ((expected != null) && (got != null)
198 && (expected.netId == got.netId)
199 && Arrays.equals(expected.eventTypes, got.eventTypes)
200 && Arrays.equals(expected.returnCodes, got.returnCodes)
201 && Arrays.equals(expected.latenciesMs, got.latenciesMs));
202 }
203}