/* * 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.wifi; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.test.suitebuilder.annotation.SmallTest; import org.junit.Before; import org.junit.Test; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; /** * Unit tests for {@link com.android.server.wifi.WifiNative}. */ @SmallTest public class WifiNativeTest { private static final int NETWORK_ID = 0; private static final String NETWORK_EXTRAS_VARIABLE = "test"; private static final Map NETWORK_EXTRAS_VALUES = new HashMap<>(); static { NETWORK_EXTRAS_VALUES.put("key1", "value1"); NETWORK_EXTRAS_VALUES.put("key2", "value2"); } private static final String NETWORK_EXTRAS_SERIALIZED = "\"%7B%22key2%22%3A%22value2%22%2C%22key1%22%3A%22value1%22%7D\""; private static final long FATE_REPORT_DRIVER_TIMESTAMP_USEC = 12345; private static final byte[] FATE_REPORT_FRAME_BYTES = new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 0, 1, 2, 3, 4, 5, 6, 7}; private static final WifiNative.TxFateReport TX_FATE_REPORT = new WifiNative.TxFateReport( WifiLoggerHal.TX_PKT_FATE_SENT, FATE_REPORT_DRIVER_TIMESTAMP_USEC, WifiLoggerHal.FRAME_TYPE_ETHERNET_II, FATE_REPORT_FRAME_BYTES ); private static final WifiNative.RxFateReport RX_FATE_REPORT = new WifiNative.RxFateReport( WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID, FATE_REPORT_DRIVER_TIMESTAMP_USEC, WifiLoggerHal.FRAME_TYPE_ETHERNET_II, FATE_REPORT_FRAME_BYTES ); private static final FrameTypeMapping[] FRAME_TYPE_MAPPINGS = new FrameTypeMapping[] { new FrameTypeMapping(WifiLoggerHal.FRAME_TYPE_UNKNOWN, "unknown", "N/A"), new FrameTypeMapping(WifiLoggerHal.FRAME_TYPE_ETHERNET_II, "data", "Ethernet"), new FrameTypeMapping(WifiLoggerHal.FRAME_TYPE_80211_MGMT, "802.11 management", "802.11 Mgmt"), new FrameTypeMapping((byte) 42, "42", "N/A") }; private static final FateMapping[] TX_FATE_MAPPINGS = new FateMapping[] { new FateMapping(WifiLoggerHal.TX_PKT_FATE_ACKED, "acked"), new FateMapping(WifiLoggerHal.TX_PKT_FATE_SENT, "sent"), new FateMapping(WifiLoggerHal.TX_PKT_FATE_FW_QUEUED, "firmware queued"), new FateMapping(WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID, "firmware dropped (invalid frame)"), new FateMapping( WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS, "firmware dropped (no bufs)"), new FateMapping( WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER, "firmware dropped (other)"), new FateMapping(WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED, "driver queued"), new FateMapping(WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID, "driver dropped (invalid frame)"), new FateMapping(WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS, "driver dropped (no bufs)"), new FateMapping(WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER, "driver dropped (other)"), new FateMapping((byte) 42, "42") }; private static final FateMapping[] RX_FATE_MAPPINGS = new FateMapping[] { new FateMapping(WifiLoggerHal.RX_PKT_FATE_SUCCESS, "success"), new FateMapping(WifiLoggerHal.RX_PKT_FATE_FW_QUEUED, "firmware queued"), new FateMapping( WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER, "firmware dropped (filter)"), new FateMapping(WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID, "firmware dropped (invalid frame)"), new FateMapping( WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS, "firmware dropped (no bufs)"), new FateMapping( WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER, "firmware dropped (other)"), new FateMapping(WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED, "driver queued"), new FateMapping( WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER, "driver dropped (filter)"), new FateMapping(WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID, "driver dropped (invalid frame)"), new FateMapping( WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS, "driver dropped (no bufs)"), new FateMapping(WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER, "driver dropped (other)"), new FateMapping((byte) 42, "42") }; private WifiNative mWifiNative; @Before public void setUp() throws Exception { final Constructor wifiNativeConstructor = WifiNative.class.getDeclaredConstructor(String.class, Boolean.TYPE); wifiNativeConstructor.setAccessible(true); mWifiNative = spy(wifiNativeConstructor.newInstance("test", true)); } /** * Verifies that setNetworkExtra() correctly writes a serialized and URL-encoded JSON object. */ @Test public void testSetNetworkExtra() { when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true); assertTrue(mWifiNative.setNetworkExtra(NETWORK_ID, NETWORK_EXTRAS_VARIABLE, NETWORK_EXTRAS_VALUES)); verify(mWifiNative).setNetworkVariable(NETWORK_ID, NETWORK_EXTRAS_VARIABLE, NETWORK_EXTRAS_SERIALIZED); } /** * Verifies that getNetworkExtra() correctly reads a serialized and URL-encoded JSON object. */ @Test public void testGetNetworkExtra() { when(mWifiNative.getNetworkVariable(NETWORK_ID, NETWORK_EXTRAS_VARIABLE)) .thenReturn(NETWORK_EXTRAS_SERIALIZED); final Map actualValues = mWifiNative.getNetworkExtra(NETWORK_ID, NETWORK_EXTRAS_VARIABLE); assertEquals(NETWORK_EXTRAS_VALUES, actualValues); } /** * Verifies that TxFateReport's constructor sets all of the TxFateReport fields. */ @Test public void testTxFateReportCtorSetsFields() { WifiNative.TxFateReport fateReport = new WifiNative.TxFateReport( WifiLoggerHal.TX_PKT_FATE_SENT, // non-zero value FATE_REPORT_DRIVER_TIMESTAMP_USEC, WifiLoggerHal.FRAME_TYPE_ETHERNET_II, // non-zero value FATE_REPORT_FRAME_BYTES ); assertEquals(WifiLoggerHal.TX_PKT_FATE_SENT, fateReport.mFate); assertEquals(FATE_REPORT_DRIVER_TIMESTAMP_USEC, fateReport.mDriverTimestampUSec); assertEquals(WifiLoggerHal.FRAME_TYPE_ETHERNET_II, fateReport.mFrameType); assertArrayEquals(FATE_REPORT_FRAME_BYTES, fateReport.mFrameBytes); } /** * Verifies that RxFateReport's constructor sets all of the RxFateReport fields. */ @Test public void testRxFateReportCtorSetsFields() { WifiNative.RxFateReport fateReport = new WifiNative.RxFateReport( WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID, // non-zero value FATE_REPORT_DRIVER_TIMESTAMP_USEC, WifiLoggerHal.FRAME_TYPE_ETHERNET_II, // non-zero value FATE_REPORT_FRAME_BYTES ); assertEquals(WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID, fateReport.mFate); assertEquals(FATE_REPORT_DRIVER_TIMESTAMP_USEC, fateReport.mDriverTimestampUSec); assertEquals(WifiLoggerHal.FRAME_TYPE_ETHERNET_II, fateReport.mFrameType); assertArrayEquals(FATE_REPORT_FRAME_BYTES, fateReport.mFrameBytes); } // Support classes for test{Tx,Rx}FateReportToString. private static class FrameTypeMapping { byte mTypeNumber; String mExpectedTypeText; String mExpectedProtocolText; FrameTypeMapping(byte typeNumber, String expectedTypeText, String expectedProtocolText) { this.mTypeNumber = typeNumber; this.mExpectedTypeText = expectedTypeText; this.mExpectedProtocolText = expectedProtocolText; } } private static class FateMapping { byte mFateNumber; String mExpectedText; FateMapping(byte fateNumber, String expectedText) { this.mFateNumber = fateNumber; this.mExpectedText = expectedText; } } /** * Verifies that FateReport.getTableHeader() prints the right header. */ @Test public void testFateReportTableHeader() { final String header = WifiNative.FateReport.getTableHeader(); assertEquals( "\nTime usec Walltime Direction Fate " + "Protocol Type Result\n" + "--------- -------- --------- ---- " + "-------- ---- ------\n", header); } /** * Verifies that TxFateReport.toTableRowString() includes the information we care about. */ @Test public void testTxFateReportToTableRowString() { WifiNative.TxFateReport fateReport = TX_FATE_REPORT; assertTrue( fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches( FATE_REPORT_DRIVER_TIMESTAMP_USEC + " " // timestamp + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} " // walltime + "TX " // direction + "sent " // fate + "Ethernet " // type + "N/A " // protocol + "N/A" // result ) ); for (FrameTypeMapping frameTypeMapping : FRAME_TYPE_MAPPINGS) { fateReport = new WifiNative.TxFateReport( WifiLoggerHal.TX_PKT_FATE_SENT, FATE_REPORT_DRIVER_TIMESTAMP_USEC, frameTypeMapping.mTypeNumber, FATE_REPORT_FRAME_BYTES ); assertTrue( fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches( FATE_REPORT_DRIVER_TIMESTAMP_USEC + " " // timestamp + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} " // walltime + "TX " // direction + "sent " // fate + frameTypeMapping.mExpectedProtocolText + " " // type + "N/A " // protocol + "N/A" // result ) ); } for (FateMapping fateMapping : TX_FATE_MAPPINGS) { fateReport = new WifiNative.TxFateReport( fateMapping.mFateNumber, FATE_REPORT_DRIVER_TIMESTAMP_USEC, WifiLoggerHal.FRAME_TYPE_80211_MGMT, FATE_REPORT_FRAME_BYTES ); assertTrue( fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches( FATE_REPORT_DRIVER_TIMESTAMP_USEC + " " // timestamp + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} " // walltime + "TX " // direction + Pattern.quote(fateMapping.mExpectedText) + " " // fate + "802.11 Mgmt " // type + "N/A " // protocol + "N/A" // result ) ); } } /** * Verifies that TxFateReport.toVerboseStringWithPiiAllowed() includes the information we care * about. */ @Test public void testTxFateReportToVerboseStringWithPiiAllowed() { WifiNative.TxFateReport fateReport = TX_FATE_REPORT; String verboseFateString = fateReport.toVerboseStringWithPiiAllowed(); assertTrue(verboseFateString.contains("Frame direction: TX")); assertTrue(verboseFateString.contains("Frame timestamp: 12345")); assertTrue(verboseFateString.contains("Frame fate: sent")); assertTrue(verboseFateString.contains("Frame type: data")); assertTrue(verboseFateString.contains("Frame protocol: Ethernet")); assertTrue(verboseFateString.contains("Frame protocol type: N/A")); assertTrue(verboseFateString.contains("Frame length: 16")); assertTrue(verboseFateString.contains( "61 62 63 64 65 66 67 68 00 01 02 03 04 05 06 07")); // hex dump // TODO(quiche): uncomment this, once b/27975149 is fixed. // assertTrue(verboseFateString.contains("abcdefgh........")); // hex dump for (FrameTypeMapping frameTypeMapping : FRAME_TYPE_MAPPINGS) { fateReport = new WifiNative.TxFateReport( WifiLoggerHal.TX_PKT_FATE_SENT, FATE_REPORT_DRIVER_TIMESTAMP_USEC, frameTypeMapping.mTypeNumber, FATE_REPORT_FRAME_BYTES ); verboseFateString = fateReport.toVerboseStringWithPiiAllowed(); assertTrue(verboseFateString.contains("Frame type: " + frameTypeMapping.mExpectedTypeText)); } for (FateMapping fateMapping : TX_FATE_MAPPINGS) { fateReport = new WifiNative.TxFateReport( fateMapping.mFateNumber, FATE_REPORT_DRIVER_TIMESTAMP_USEC, WifiLoggerHal.FRAME_TYPE_80211_MGMT, FATE_REPORT_FRAME_BYTES ); verboseFateString = fateReport.toVerboseStringWithPiiAllowed(); assertTrue(verboseFateString.contains("Frame fate: " + fateMapping.mExpectedText)); } } /** * Verifies that RxFateReport.toTableRowString() includes the information we care about. */ @Test public void testRxFateReportToTableRowString() { WifiNative.RxFateReport fateReport = RX_FATE_REPORT; assertTrue( fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches( FATE_REPORT_DRIVER_TIMESTAMP_USEC + " " // timestamp + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} " // walltime + "RX " // direction + Pattern.quote("firmware dropped (invalid frame) ") // fate + "Ethernet " // type + "N/A " // protocol + "N/A" // result ) ); // FrameTypeMappings omitted, as they're the same as for TX. for (FateMapping fateMapping : RX_FATE_MAPPINGS) { fateReport = new WifiNative.RxFateReport( fateMapping.mFateNumber, FATE_REPORT_DRIVER_TIMESTAMP_USEC, WifiLoggerHal.FRAME_TYPE_80211_MGMT, FATE_REPORT_FRAME_BYTES ); assertTrue( fateReport.toTableRowString().replaceAll("\\s+", " ").trim().matches( FATE_REPORT_DRIVER_TIMESTAMP_USEC + " " // timestamp + "\\d{2}:\\d{2}:\\d{2}\\.\\d{3} " // walltime + "RX " // direction + Pattern.quote(fateMapping.mExpectedText) + " " // fate + "802.11 Mgmt " // type + "N/A " // protocol + "N/A" // result ) ); } } /** * Verifies that RxFateReport.toVerboseStringWithPiiAllowed() includes the information we care * about. */ @Test public void testRxFateReportToVerboseStringWithPiiAllowed() { WifiNative.RxFateReport fateReport = RX_FATE_REPORT; String verboseFateString = fateReport.toVerboseStringWithPiiAllowed(); assertTrue(verboseFateString.contains("Frame direction: RX")); assertTrue(verboseFateString.contains("Frame timestamp: 12345")); assertTrue(verboseFateString.contains("Frame fate: firmware dropped (invalid frame)")); assertTrue(verboseFateString.contains("Frame type: data")); assertTrue(verboseFateString.contains("Frame protocol: Ethernet")); assertTrue(verboseFateString.contains("Frame protocol type: N/A")); assertTrue(verboseFateString.contains("Frame length: 16")); assertTrue(verboseFateString.contains( "61 62 63 64 65 66 67 68 00 01 02 03 04 05 06 07")); // hex dump // TODO(quiche): uncomment this, once b/27975149 is fixed. // assertTrue(verboseFateString.contains("abcdefgh........")); // hex dump // FrameTypeMappings omitted, as they're the same as for TX. for (FateMapping fateMapping : RX_FATE_MAPPINGS) { fateReport = new WifiNative.RxFateReport( fateMapping.mFateNumber, FATE_REPORT_DRIVER_TIMESTAMP_USEC, WifiLoggerHal.FRAME_TYPE_80211_MGMT, FATE_REPORT_FRAME_BYTES ); verboseFateString = fateReport.toVerboseStringWithPiiAllowed(); assertTrue(verboseFateString.contains("Frame fate: " + fateMapping.mExpectedText)); } } /** * Verifies that startPktFateMonitoring returns false when HAL is not started. */ @Test public void testStartPktFateMonitoringReturnsFalseWhenHalIsNotStarted() { assertFalse(mWifiNative.isHalStarted()); assertFalse(mWifiNative.startPktFateMonitoring()); } /** * Verifies that getTxPktFates returns error when HAL is not started. */ @Test public void testGetTxPktFatesReturnsErrorWhenHalIsNotStarted() { WifiNative.TxFateReport[] fateReports = null; assertFalse(mWifiNative.isHalStarted()); assertFalse(mWifiNative.getTxPktFates(fateReports)); } /** * Verifies that getRxPktFates returns error when HAL is not started. */ @Test public void testGetRxPktFatesReturnsErrorWhenHalIsNotStarted() { WifiNative.RxFateReport[] fateReports = null; assertFalse(mWifiNative.isHalStarted()); assertFalse(mWifiNative.getRxPktFates(fateReports)); } // TODO(quiche): Add tests for the success cases (when HAL has been started). Specifically: // - testStartPktFateMonitoringCallsHalIfHalIsStarted() // - testGetTxPktFatesCallsHalIfHalIsStarted() // - testGetRxPktFatesCallsHalIfHalIsStarted() // // Adding these tests is difficult to do at the moment, because we can't mock out the HAL // itself. Also, we can't mock out the native methods, because those methods are private. // b/28005116. /** Verifies that getDriverStateDumpNative returns null when HAL is not started. */ @Test public void testGetDriverStateDumpReturnsNullWhenHalIsNotStarted() { assertEquals(null, mWifiNative.getDriverStateDump()); } // TODO(b/28005116): Add test for the success case of getDriverStateDump(). }