/* * Copyright 2018 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 com.android.server.wifi.util.NativeUtil.removeEnclosingQuotes; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.net.MacAddress; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Unit tests for {@link com.android.server.wifi.WifiCandidates}. */ @SmallTest public class WifiCandidatesTest { @Mock ScanDetail mScanDetail1; @Mock ScanDetail mScanDetail2; @Mock WifiScoreCard mWifiScoreCard; @Mock WifiScoreCard.PerBssid mPerBssid; ScanResult mScanResult1; ScanResult mScanResult2; WifiConfiguration mConfig1; WifiConfiguration mConfig2; WifiCandidates mWifiCandidates; /** * Sets up for unit test */ @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mWifiCandidates = new WifiCandidates(mWifiScoreCard); mConfig1 = WifiConfigurationTestUtil.createOpenNetwork(); mScanResult1 = new ScanResult() {{ SSID = removeEnclosingQuotes(mConfig1.SSID); capabilities = "[ESS]"; BSSID = "00:00:00:00:00:01"; }}; mConfig2 = WifiConfigurationTestUtil.createEphemeralNetwork(); mScanResult2 = new ScanResult() {{ SSID = removeEnclosingQuotes(mConfig2.SSID); capabilities = "[ESS]"; }}; doReturn(mScanResult1).when(mScanDetail1).getScanResult(); doReturn(mScanResult2).when(mScanDetail2).getScanResult(); doReturn(mPerBssid).when(mWifiScoreCard).lookupBssid(any(), any()); } /** * Test for absence of null pointer exceptions */ @Test public void testDontDieFromNulls() throws Exception { mWifiCandidates.add(null, mConfig1, 1, 42); mWifiCandidates.add(mScanDetail1, null, 2, 16); doReturn(null).when(mScanDetail2).getScanResult(); mWifiCandidates.add(mScanDetail2, mConfig2, 3, 314, 1.0); assertFalse(mWifiCandidates.remove(null)); assertEquals(0, mWifiCandidates.size()); } /** * Add just one thing */ @Test public void testAddJustOne() throws Exception { assertTrue(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0)); assertEquals(1, mWifiCandidates.size()); assertEquals(0, mWifiCandidates.getFaultCount()); assertNull(mWifiCandidates.getLastFault()); verify(mPerBssid).setNetworkConfigId(eq(mConfig1.networkId)); } /** * Make sure we catch SSID mismatch due to quoting error */ @Test public void testQuotingBotch() throws Exception { // Unfortunately ScanResult.SSID is not quoted; make sure we catch that mScanResult1.SSID = mConfig1.SSID; mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0); // Should not have added this one assertEquals(0, mWifiCandidates.size()); // The failure should have been recorded assertEquals(1, mWifiCandidates.getFaultCount()); // The record of the failure should contain the culprit String blah = mWifiCandidates.getLastFault().toString(); assertTrue(blah, blah.contains(mConfig1.SSID)); // Now check that we can clear the faults mWifiCandidates.clearFaults(); assertEquals(0, mWifiCandidates.getFaultCount()); assertNull(mWifiCandidates.getLastFault()); } /** * Test Key equals and hashCode methods */ @Test public void testKeyEquivalence() throws Exception { ScanResultMatchInfo matchInfo1 = ScanResultMatchInfo.fromWifiConfiguration(mConfig1); ScanResultMatchInfo matchInfo1Prime = ScanResultMatchInfo.fromWifiConfiguration(mConfig1); ScanResultMatchInfo matchInfo2 = ScanResultMatchInfo.fromWifiConfiguration(mConfig2); assertFalse(matchInfo1 == matchInfo1Prime); // Checking assumption MacAddress mac1 = MacAddress.createRandomUnicastAddress(); MacAddress mac2 = MacAddress.createRandomUnicastAddress(); assertNotEquals(mac1, mac2); // really tiny probablility of failing here WifiCandidates.Key key1 = new WifiCandidates.Key(matchInfo1, mac1, 1); assertFalse(key1.equals(null)); assertFalse(key1.equals((Integer) 0)); // Same inputs should give equal results assertEquals(key1, new WifiCandidates.Key(matchInfo1, mac1, 1)); // Equal inputs should give equal results assertEquals(key1, new WifiCandidates.Key(matchInfo1Prime, mac1, 1)); // Hash codes of equal things should be equal assertEquals(key1.hashCode(), key1.hashCode()); assertEquals(key1.hashCode(), new WifiCandidates.Key(matchInfo1, mac1, 1).hashCode()); assertEquals(key1.hashCode(), new WifiCandidates.Key(matchInfo1Prime, mac1, 1).hashCode()); // Unequal inputs should give unequal results assertFalse(key1.equals(new WifiCandidates.Key(matchInfo2, mac1, 1))); assertFalse(key1.equals(new WifiCandidates.Key(matchInfo1, mac2, 1))); assertFalse(key1.equals(new WifiCandidates.Key(matchInfo1, mac1, 2))); } /** * Test that picky mode works */ @Test public void testPickyMode() throws Exception { // Set picky mode, make sure that it returns the object itself (so that // method chaining may be used). assertTrue(mWifiCandidates == mWifiCandidates.setPicky(true)); try { mScanResult1.SSID = mConfig1.SSID; // As in testQuotingBotch() mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0); fail("Exception not raised in picky mode"); } catch (IllegalArgumentException e) { assertEquals(1, mWifiCandidates.getFaultCount()); assertEquals(e, mWifiCandidates.getLastFault()); } } /** * Try cases where we don't overwrite existing candidates */ @Test public void testNoOverwriteCases() throws Exception { // Setup is to add the first candidate mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0); assertEquals(1, mWifiCandidates.size()); // Same evaluator, same score. Should not add. assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14, 0.0)); assertEquals(0, mWifiCandidates.getFaultCount()); // But not considered a fault // Same evaluator, lower score. Should not add. assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 13, 0.0)); assertEquals(0, mWifiCandidates.getFaultCount()); // Also not a fault // Later evaluator. Should not add (regardless of score). assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 5, 13)); assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 5, 15)); assertEquals(0, mWifiCandidates.getFaultCount()); // Still no faults // Evaluator out of order. Should not add (regardless of score). assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 1, 12)); assertNotNull(mWifiCandidates.getLastFault()); // This one is considered a caller error assertFalse(mWifiCandidates.add(mScanDetail1, mConfig1, 1, 15)); assertEquals(2, mWifiCandidates.getFaultCount()); // After all that, only one candidate should be there. assertEquals(1, mWifiCandidates.size()); } /** * BSSID validation */ @Test public void testBssidValidation() throws Exception { // Null BSSID. mScanResult1.BSSID = null; mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14); assertTrue("Expecting NPE, got " + mWifiCandidates.getLastFault(), mWifiCandidates.getLastFault() instanceof NullPointerException); // Malformed BSSID mScanResult1.BSSID = "NotaBssid!"; mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14); assertTrue("Expecting IAE, got " + mWifiCandidates.getLastFault(), mWifiCandidates.getLastFault() instanceof IllegalArgumentException); assertEquals(0, mWifiCandidates.size()); } /** * Add candidate BSSIDs in the same network, then remove them */ @Test public void testTwoBssids() throws Exception { // Make a duplicate of the first config mConfig2 = new WifiConfiguration(mConfig1); // Make a second scan result, same network, different BSSID. mScanResult2.SSID = mScanResult1.SSID; mScanResult2.BSSID = mScanResult1.BSSID.replace('1', '2'); // Add both mWifiCandidates.add(mScanDetail1, mConfig1, 2, 14); mWifiCandidates.add(mScanDetail2, mConfig2, 2, 14); // We expect them both to be there assertEquals(2, mWifiCandidates.size()); // But just one group assertEquals(1, mWifiCandidates.getGroupedCandidates().size()); // Now remove them one at a time WifiCandidates.Candidate c1, c2; c1 = mWifiCandidates.getGroupedCandidates().iterator().next().iterator().next(); assertTrue(mWifiCandidates.remove(c1)); assertEquals(1, mWifiCandidates.size()); assertEquals(1, mWifiCandidates.getGroupedCandidates().size()); // Should not be able to remove the one that isn't there assertFalse(mWifiCandidates.remove(c1)); // Remove the other one, too c2 = mWifiCandidates.getGroupedCandidates().iterator().next().iterator().next(); assertTrue(mWifiCandidates.remove(c2)); assertFalse(mWifiCandidates.remove(c2)); assertEquals(0, mWifiCandidates.size()); assertEquals(0, mWifiCandidates.getGroupedCandidates().size()); // Check that we have the right scan details, in either order assertTrue((mScanDetail1 == c1.getScanDetail() && mScanDetail2 == c2.getScanDetail()) || (mScanDetail2 == c1.getScanDetail() && mScanDetail1 == c2.getScanDetail())); } /** * Test replacing a candidate with a higher scoring one */ @Test public void testReplace() throws Exception { // Make a duplicate of the first config mConfig2 = new WifiConfiguration(mConfig1); // And the scan result mScanResult2.SSID = mScanResult1.SSID; mScanResult2.BSSID = mScanResult1.BSSID; // Try adding them both, the higher-scoring one second assertTrue(mWifiCandidates.add(mScanDetail2, mConfig2, 2, 14)); assertTrue(mWifiCandidates.add(mScanDetail1, mConfig1, 2, 15)); // Only one should survive assertEquals(1, mWifiCandidates.size()); // And no faults assertEquals(0, mWifiCandidates.getFaultCount()); // Make sure we kept the one with a higher evaluatorScore WifiCandidates.Candidate c; c = mWifiCandidates.getGroupedCandidates().iterator().next().iterator().next(); assertEquals(15, c.getEvaluatorScore()); } }