/* * Copyright (C) 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 org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.net.wifi.WifiInfo; import android.os.Handler; import android.provider.Settings; import android.support.test.filters.SmallTest; import com.android.internal.R; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; /** * Unit tests for {@link com.android.server.wifi.ScoringParams}. */ @SmallTest public class ScoringParamsTest { private static final String EXPECTED_DEFAULTS = "rssi2=-83:-80:-73:-60,rssi5=-80:-77:-70:-57,pps=0:1:100,horizon=15,nud=8,expid=0"; private ScoringParams mScoringParams; /** * Sets up for unit test */ @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); setUpResources(mResources); when(mContext.getResources()).thenReturn(mResources); mScoringParams = new ScoringParams(); } /** * Check that thresholds are properly ordered, and in range. */ private void checkThresholds(int frequency) { assertTrue(-127 < mScoringParams.getExitRssi(frequency)); assertTrue(mScoringParams.getExitRssi(frequency) <= mScoringParams.getEntryRssi(frequency)); assertTrue(mScoringParams.getEntryRssi(frequency) <= mScoringParams.getSufficientRssi(frequency)); assertTrue(mScoringParams.getSufficientRssi(frequency) <= mScoringParams.getGoodRssi(frequency)); assertTrue(mScoringParams.getGoodRssi(frequency) < 0); } /** * Test basic constructor */ @Test public void testBasicConstructor() throws Exception { mScoringParams = new ScoringParams(); checkThresholds(2412); checkThresholds(5020); assertEquals(15, mScoringParams.getHorizonSeconds()); } /** * Test toString */ @Test public void testToString() throws Exception { mScoringParams = new ScoringParams(); String expect = EXPECTED_DEFAULTS; String actual = mScoringParams.toString(); assertEquals(expect, actual); } /** * Test complete update */ @Test public void testUpdateEverything() throws Exception { mScoringParams = new ScoringParams(); String params = EXPECTED_DEFAULTS.replaceAll("8", "9"); assertTrue(mScoringParams.update(params)); assertEquals(params, mScoringParams.toString()); } /** * Test partial update */ @Test public void testPartialUpdate() throws Exception { mScoringParams = new ScoringParams(); String before = mScoringParams.toString(); String partial = "rssi5=-88:-77:-66:-55"; assertFalse(before.contains(partial)); assertTrue(mScoringParams.update(partial)); String after = mScoringParams.toString(); assertTrue(after + " should contain " + partial, after.contains(partial)); assertEquals(before.replaceAll("rssi5=[^,]*,", partial + ","), after); } /** * Test some failed updates */ @Test public void testUpdateFail() throws Exception { mScoringParams = new ScoringParams(); String before = mScoringParams.toString(); assertFalse(mScoringParams.update("word")); assertFalse(mScoringParams.update("42")); assertFalse(mScoringParams.update(" ")); assertFalse(mScoringParams.update("horizon=flat")); assertFalse(mScoringParams.update("horizon=+")); assertFalse(mScoringParams.update(",,,,,,,,,,,,,,,,,,")); assertFalse(mScoringParams.update("rssi2=-86")); assertFalse(mScoringParams.update("rssi2=-99:-88:-77:-66:-55")); assertFalse(mScoringParams.update("rssi5=one:two:three:four")); assertFalse(mScoringParams.update("nud=-1")); assertFalse(mScoringParams.update("nud=11")); assertFalse(mScoringParams.update("pps=1:2:3:4")); assertFalse(mScoringParams.update("pps=1:0:9")); assertFalse(mScoringParams.update("pps=packets-per-second")); assertEquals(before, mScoringParams.toString()); } /** * Duplicate keys not allowed */ @Test public void testDuplicateKeysNotAllowed() throws Exception { mScoringParams = new ScoringParams(); String before = mScoringParams.toString(); String partial = "rssi5=-88:-77:-66:-55"; assertFalse(mScoringParams.update(partial + "," + partial)); assertEquals(before, mScoringParams.toString()); } /** * Range and ordering checks for rssi values should work */ @Test public void testRssiRangeChecks() throws Exception { mScoringParams = new ScoringParams(); String before = mScoringParams.toString(); assertFalse("Must be negative", mScoringParams.update("rssi2=0:1:2:3")); assertFalse("Must be ordered", mScoringParams.update("rssi5=-88:-89:-66:-55")); assertFalse("Must be not too negative", mScoringParams.update("rssi5=-128:-77:-66:-55")); String what = "rssi5=" + WifiInfo.INVALID_RSSI + ":-77:-66:-55"; assertFalse(what, mScoringParams.update(what)); assertEquals(before, mScoringParams.toString()); } /** * Range checks should catch wild values */ @Test public void testScalarRangeChecks() throws Exception { mScoringParams = new ScoringParams(); assertTrue(mScoringParams.update("horizon=60")); assertFalse(mScoringParams.update("horizon=61")); assertTrue(mScoringParams.update("horizon=-9")); // Not recommended, but shouldn't break assertFalse(mScoringParams.update("horizon=-10")); } /** * Test that nud is hooked up */ @Test public void testNudKnob() throws Exception { mScoringParams = new ScoringParams(); assertTrue(mScoringParams.update("nud=6")); assertEquals(6, mScoringParams.getNudKnob()); } /** * Test that expid is hooked up */ @Test public void testExperimentIdentifier() throws Exception { mScoringParams = new ScoringParams(); assertEquals(0, mScoringParams.getExperimentIdentifier()); assertTrue(mScoringParams.update("expid=99")); assertEquals(99, mScoringParams.getExperimentIdentifier()); } /** * Check character set */ @Test public void testBadCharacterChecks() throws Exception { mScoringParams = new ScoringParams(); String before = mScoringParams.toString(); assertFalse(mScoringParams.update("\007ding\007=7")); assertTrue(mScoringParams.update("unknown_key=14,ThatThing=987654321")); assertEquals(before, mScoringParams.toString()); } /** * Sanitize should hide garbage characters and truncate length */ @Test public void testSanitize() throws Exception { mScoringParams = new ScoringParams(); String longKey = "A123456789_123456789_123456789_123456789_123456789"; String longVal = "9.99999999999999999999999999999999999999999999999"; String param100 = longKey + "=" + longVal; assertEquals(param100, mScoringParams.sanitize(param100)); assertEquals(101, mScoringParams.sanitize(param100 + "00000000000").length()); assertEquals(param100.substring(0, 90), mScoringParams.sanitize(param100 + "00000000000").substring(0, 90)); assertEquals("q?=???", mScoringParams.sanitize("q\b= ~[")); assertEquals("", mScoringParams.sanitize(null)); } /** * Test that empty updates are OK */ @Test public void testEmptyUpdate() throws Exception { mScoringParams = new ScoringParams(); String before = mScoringParams.toString(); assertTrue(mScoringParams.update("")); assertTrue(mScoringParams.update(null)); assertEquals(before, mScoringParams.toString()); } /** * Tests for obtaining values from device configuration (config.xml) */ int mBad2GHz, mEntry2GHz, mSufficient2GHz, mGood2GHz; int mBad5GHz, mEntry5GHz, mSufficient5GHz, mGood5GHz; @Mock Context mContext; @Spy private MockResources mResources = new MockResources(); private int setupIntegerResource(int resourceName, int value) { doReturn(value).when(mResources).getInteger(resourceName); return value; } /** * Sets up resource values for testing * * See frameworks/base/core/res/res/values/config.xml */ private void setUpResources(Resources resources) { mBad2GHz = setupIntegerResource( R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz, -88); mEntry2GHz = setupIntegerResource( R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz, -77); mSufficient2GHz = setupIntegerResource( R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz, -66); mGood2GHz = setupIntegerResource( R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz, -55); mBad5GHz = setupIntegerResource( R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz, -80); mEntry5GHz = setupIntegerResource( R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz, -70); mSufficient5GHz = setupIntegerResource( R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz, -60); mGood5GHz = setupIntegerResource( R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz, -50); } /** * Check that we get the config.xml values, if that's what we want */ @Test public void testContextConstructor() throws Exception { mScoringParams = new ScoringParams(mContext); assertEquals(mBad2GHz, mScoringParams.getExitRssi(2412)); assertEquals(mEntry2GHz, mScoringParams.getEntryRssi(2480)); assertEquals(mSufficient2GHz, mScoringParams.getSufficientRssi(2400)); assertEquals(mGood2GHz, mScoringParams.getGoodRssi(2499)); assertEquals(mGood2GHz, mScoringParams.getGoodRssi(ScoringParams.BAND2)); assertEquals(mBad5GHz, mScoringParams.getExitRssi(5000)); assertEquals(mEntry5GHz, mScoringParams.getEntryRssi(5010)); assertEquals(mSufficient5GHz, mScoringParams.getSufficientRssi(5100)); assertEquals(mGood5GHz, mScoringParams.getGoodRssi(5678)); assertEquals(mGood5GHz, mScoringParams.getGoodRssi(ScoringParams.BAND5)); } /** * Additional mocks for handling Settings */ @Mock FrameworkFacade mFrameworkFacade; @Mock Handler mHandler; /** * Test getting updates from Settings * * Exercises the ContentObserver notification path */ @Test public void testFullConstructorWithUpdatesFromSettings() throws Exception { ArgumentCaptor captor = ArgumentCaptor.forClass(ContentObserver.class); when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS)) .thenReturn(null); mScoringParams = new ScoringParams(mContext, mFrameworkFacade, mHandler); verify(mFrameworkFacade) .registerContentObserver(eq(mContext), any(), anyBoolean(), captor.capture()); String before = mScoringParams.toString(); String changed = before.replace('8', '9'); assertFalse(changed.equals(before)); when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS)) .thenReturn(changed); captor.getValue().onChange(/*selfChange*/ false); assertEquals(changed, mScoringParams.toString()); when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS)) .thenReturn(""); captor.getValue().onChange(/*selfChange*/ false); assertEquals(before, mScoringParams.toString()); } @Test public void testBadSettings() throws Exception { ArgumentCaptor captor = ArgumentCaptor.forClass(ContentObserver.class); when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS)) .thenReturn(null); mScoringParams = new ScoringParams(mContext, mFrameworkFacade, mHandler); verify(mFrameworkFacade) .registerContentObserver(eq(mContext), any(), anyBoolean(), captor.capture()); String before = mScoringParams.toString(); String garbage = "what??"; when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS)) .thenReturn(garbage); captor.getValue().onChange(/*selfChange*/ false); assertEquals(before, mScoringParams.toString()); } }