diff --git a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java new file mode 100644 index 0000000000..3fc89b9ff1 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity; + +import android.content.Context; +import android.net.ConnectivityMetricsEvent; +import android.net.IIpConnectivityMetrics; +import android.net.metrics.ApfStats; +import android.net.metrics.DefaultNetworkEvent; +import android.net.metrics.DhcpClientEvent; +import android.net.metrics.IpConnectivityLog; +import android.net.metrics.IpManagerEvent; +import android.net.metrics.IpReachabilityEvent; +import android.net.metrics.RaEvent; +import android.net.metrics.ValidationProbeEvent; +import android.os.Parcelable; +import android.util.Base64; +import com.android.server.connectivity.metrics.IpConnectivityLogClass; +import com.google.protobuf.nano.MessageNano; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import junit.framework.TestCase; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class IpConnectivityMetricsTest extends TestCase { + static final IpReachabilityEvent FAKE_EV = + new IpReachabilityEvent("wlan0", IpReachabilityEvent.NUD_FAILED); + + @Mock Context mCtx; + @Mock IIpConnectivityMetrics mMockService; + + IpConnectivityMetrics mService; + + public void setUp() { + MockitoAnnotations.initMocks(this); + mService = new IpConnectivityMetrics(mCtx); + } + + public void testLoggingEvents() throws Exception { + IpConnectivityLog logger = new IpConnectivityLog(mMockService); + + assertTrue(logger.log(1, FAKE_EV)); + assertTrue(logger.log(2, FAKE_EV)); + assertTrue(logger.log(3, FAKE_EV)); + + List got = verifyEvents(3); + assertEventsEqual(expectedEvent(1), got.get(0)); + assertEventsEqual(expectedEvent(2), got.get(1)); + assertEventsEqual(expectedEvent(3), got.get(2)); + } + + public void testLoggingEventsWithMultipleCallers() throws Exception { + IpConnectivityLog logger = new IpConnectivityLog(mMockService); + + final int nCallers = 10; + final int nEvents = 10; + for (int n = 0; n < nCallers; n++) { + final int i = n; + new Thread() { + public void run() { + for (int j = 0; j < nEvents; j++) { + assertTrue(logger.log(i * 100 + j, FAKE_EV)); + } + } + }.start(); + } + + List got = verifyEvents(nCallers * nEvents, 100); + Collections.sort(got, EVENT_COMPARATOR); + Iterator iter = got.iterator(); + for (int i = 0; i < nCallers; i++) { + for (int j = 0; j < nEvents; j++) { + int expectedTimestamp = i * 100 + j; + assertEventsEqual(expectedEvent(expectedTimestamp), iter.next()); + } + } + } + + public void testBufferFlushing() { + String output1 = getdump("flush"); + assertEquals("", output1); + + new IpConnectivityLog(mService.impl).log(1, FAKE_EV); + String output2 = getdump("flush"); + assertFalse("".equals(output2)); + + String output3 = getdump("flush"); + assertEquals("", output3); + } + + public void testEndToEndLogging() { + IpConnectivityLog logger = new IpConnectivityLog(mService.impl); + + Parcelable[] events = { + new IpReachabilityEvent("wlan0", IpReachabilityEvent.NUD_FAILED), + new DhcpClientEvent("wlan0", "SomeState", 192), + new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false), + new IpManagerEvent("wlan0", IpManagerEvent.PROVISIONING_OK, 5678), + new ValidationProbeEvent(120, 40730, ValidationProbeEvent.PROBE_HTTP, 204), + new ApfStats(45000, 10, 2, 2, 1, 2, 4, 2048), + new RaEvent(2000, 400, 300, -1, 1000, -1) + }; + + for (int i = 0; i < events.length; i++) { + logger.log(100 * (i + 1), events[i]); + } + + String want = joinLines( + "dropped_events: 0", + "events <", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"wlan0\"", + " >", + " time_ms: 100", + ">", + "events <", + " dhcp_event <", + " duration_ms: 192", + " error_code: 0", + " if_name: \"wlan0\"", + " state_transition: \"SomeState\"", + " >", + " time_ms: 200", + ">", + "events <", + " default_network_event <", + " network_id <", + " network_id: 102", + " >", + " previous_network_id <", + " network_id: 101", + " >", + " previous_network_ip_support: 1", + " transport_types: 1", + " transport_types: 2", + " transport_types: 3", + " >", + " time_ms: 300", + ">", + "events <", + " ip_provisioning_event <", + " event_type: 1", + " if_name: \"wlan0\"", + " latency_ms: 5678", + " >", + " time_ms: 400", + ">", + "events <", + " time_ms: 500", + " validation_probe_event <", + " latency_ms: 40730", + " network_id <", + " network_id: 120", + " >", + " probe_result: 204", + " probe_type: 1", + " >", + ">", + "events <", + " apf_statistics <", + " dropped_ras: 2", + " duration_ms: 45000", + " matching_ras: 2", + " max_program_size: 2048", + " parse_errors: 2", + " program_updates: 4", + " received_ras: 10", + " zero_lifetime_ras: 1", + " >", + " time_ms: 600", + ">", + "events <", + " ra_event <", + " dnssl_lifetime: -1", + " prefix_preferred_lifetime: 300", + " prefix_valid_lifetime: 400", + " rdnss_lifetime: 1000", + " route_info_lifetime: -1", + " router_lifetime: 2000", + " >", + " time_ms: 700", + ">"); + + verifySerialization(want, getdump("flush")); + } + + String getdump(String ... command) { + StringWriter buffer = new StringWriter(); + PrintWriter writer = new PrintWriter(buffer); + mService.impl.dump(null, writer, command); + return buffer.toString(); + } + + List verifyEvents(int n, int timeoutMs) throws Exception { + ArgumentCaptor captor = + ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); + verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture()); + return captor.getAllValues(); + } + + List verifyEvents(int n) throws Exception { + return verifyEvents(n, 10); + } + + static void verifySerialization(String want, String output) { + try { + byte[] got = Base64.decode(output, Base64.DEFAULT); + IpConnectivityLogClass.IpConnectivityLog log = + new IpConnectivityLogClass.IpConnectivityLog(); + MessageNano.mergeFrom(log, got); + assertEquals(want, log.toString()); + } catch (Exception e) { + fail(e.toString()); + } + } + + static String joinLines(String ... elems) { + StringBuilder b = new StringBuilder(); + for (String s : elems) { + b.append(s).append("\n"); + } + return b.toString(); + } + + static ConnectivityMetricsEvent expectedEvent(int timestamp) { + return new ConnectivityMetricsEvent((long)timestamp, 0, 0, FAKE_EV); + } + + /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */ + static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) { + assertEquals(expected.timestamp, got.timestamp); + assertEquals(expected.componentTag, got.componentTag); + assertEquals(expected.eventTag, got.eventTag); + assertEquals(expected.data, got.data); + } + + static final Comparator EVENT_COMPARATOR = + new Comparator() { + @Override + public int compare(ConnectivityMetricsEvent ev1, ConnectivityMetricsEvent ev2) { + return (int) (ev1.timestamp - ev2.timestamp); + } + }; +}