From 8b1926888fa39f1e9487b510204bf475a12ca970 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 2 May 2017 12:06:32 +0900 Subject: [PATCH 1/2] Improve testing of IP address display code. This is a test-only change whose goal is to increase our confidence in future changes that touch the actual code. Bug: 62171690 Test: make -j64 RunSettingsRoboTests Change-Id: Ic5122453c9fec2b166fcfe0c5c33be2705c10906 --- .../WifiDetailPreferenceControllerTest.java | 202 ++++++++++++++---- 1 file changed, 164 insertions(+), 38 deletions(-) diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java index 208c1570c03..14556185089 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java @@ -75,9 +75,11 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @@ -89,9 +91,6 @@ public class WifiDetailPreferenceControllerTest { private static final String MAC_ADDRESS = WifiInfo.DEFAULT_MAC_ADDRESS; private static final String SECURITY = "None"; - private InetAddress mIpv4Address; - private Inet6Address mIpv6Address; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) private PreferenceScreen mockScreen; @@ -124,31 +123,71 @@ public class WifiDetailPreferenceControllerTest { @Captor private ArgumentCaptor mCallbackCaptor; @Captor private ArgumentCaptor mForgetClickListener; + @Captor private ArgumentCaptor mIpv6AddressCaptor; private Context mContext = RuntimeEnvironment.application; private Lifecycle mLifecycle; private LinkProperties mLinkProperties; private WifiDetailPreferenceController mController; + // This class exists so that these values can be made static final. They can't be static final + // members of the test class, because any attempt to call IpPrefix or RouteInfo constructors + // during static initialization of the test class results in NoSuchMethorError being thrown + // when the test is run. + private static class Constants { + static final int IPV4_PREFIXLEN = 25; + static final LinkAddress IPV4_ADDR; + static final Inet4Address IPV4_GATEWAY; + static final RouteInfo IPV4_DEFAULT; + static final RouteInfo IPV4_SUBNET; + static final LinkAddress IPV6_LINKLOCAL; + static final LinkAddress IPV6_GLOBAL1; + static final LinkAddress IPV6_GLOBAL2; + static final InetAddress IPV4_DNS1; + static final InetAddress IPV4_DNS2; + static final InetAddress IPV6_DNS; + + private static LinkAddress ipv6LinkAddress(String addr) throws UnknownHostException { + return new LinkAddress(InetAddress.getByName(addr), 64); + } + + private static LinkAddress ipv4LinkAddress(String addr, int prefixlen) + throws UnknownHostException { + return new LinkAddress(InetAddress.getByName(addr), prefixlen); + } + + static { + try { + // We create our test constants in these roundabout ways because the robolectric + // shadows don't contain NetworkUtils.parseNumericAddress and other utility methods, + // so the easy ways to do things fail with NoSuchMethodError. + IPV4_ADDR = ipv4LinkAddress("192.0.2.2", IPV4_PREFIXLEN); + IPV4_GATEWAY = (Inet4Address) InetAddress.getByName("192.0.2.127"); + + final Inet4Address any4 = (Inet4Address) InetAddress.getByName("0.0.0.0"); + IpPrefix subnet = new IpPrefix(IPV4_ADDR.getAddress(), IPV4_PREFIXLEN); + IPV4_SUBNET = new RouteInfo(subnet, any4); + IPV4_DEFAULT = new RouteInfo(new IpPrefix(any4, 0), IPV4_GATEWAY); + + IPV6_LINKLOCAL = ipv6LinkAddress("fe80::211:25ff:fef8:7cb2%1"); + IPV6_GLOBAL1 = ipv6LinkAddress("2001:db8:1::211:25ff:fef8:7cb2"); + IPV6_GLOBAL2 = ipv6LinkAddress("2001:db8:1::3dfe:8902:f98f:739d"); + + IPV4_DNS1 = InetAddress.getByName("8.8.8.8"); + IPV4_DNS2 = InetAddress.getByName("8.8.4.4"); + IPV6_DNS = InetAddress.getByName("2001:4860:4860::64"); + } catch (UnknownHostException e) { + throw new RuntimeException("Invalid hardcoded IP addresss: " + e); + } + } + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); mLifecycle = new Lifecycle(); - try { - mIpv4Address = InetAddress.getByAddress( - new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255 }); - mIpv6Address = Inet6Address.getByAddress( - "123", /* host */ - new byte[] { - (byte) 0xFE, (byte) 0x80, 0, 0, 0, 0, 0, 0, 0x02, 0x11, 0x25, - (byte) 0xFF, (byte) 0xFE, (byte) 0xF8, (byte) 0x7C, (byte) 0xB2}, - 1 /*scope id */); - } catch (UnknownHostException e) { - throw new RuntimeException(e); - } - when(mockAccessPoint.getConfig()).thenReturn(mockWifiConfig); when(mockAccessPoint.getLevel()).thenReturn(LEVEL); when(mockAccessPoint.getSecurityString(false)).thenReturn(SECURITY); @@ -172,6 +211,8 @@ public class WifiDetailPreferenceControllerTest { when(mockFragment.getActivity()).thenReturn(mockActivity); + when(mockIpv6AddressCategory.addPreference(mIpv6AddressCaptor.capture())).thenReturn(true); + setupMockedPreferenceScreen(); mController = newWifiDetailPreferenceController(); } @@ -330,26 +371,23 @@ public class WifiDetailPreferenceControllerTest { @Test public void ipAddressPref_shouldHaveDetailTextSet() { - LinkAddress ipv4Address = new LinkAddress(mIpv4Address, 32); - - mLinkProperties.addLinkAddress(ipv4Address); + mLinkProperties.addLinkAddress(Constants.IPV4_ADDR); mController.displayPreference(mockScreen); - verify(mockIpAddressPref).setDetailText(mIpv4Address.getHostAddress()); + verify(mockIpAddressPref).setDetailText(Constants.IPV4_ADDR.getAddress().getHostAddress()); } @Test public void gatewayAndSubnet_shouldHaveDetailTextSet() { - int prefixLength = 24; - IpPrefix subnet = new IpPrefix(mIpv4Address, prefixLength); - InetAddress gateway = mIpv4Address; - mLinkProperties.addRoute(new RouteInfo(subnet, gateway)); + mLinkProperties.addLinkAddress(Constants.IPV4_ADDR); + mLinkProperties.addRoute(Constants.IPV4_DEFAULT); + mLinkProperties.addRoute(Constants.IPV4_SUBNET); mController.displayPreference(mockScreen); - verify(mockSubnetPref).setDetailText("255.255.255.0"); - verify(mockGatewayPref).setDetailText(mIpv4Address.getHostAddress()); + verify(mockSubnetPref).setDetailText("255.255.255.128"); + verify(mockGatewayPref).setDetailText("192.0.2.127"); } @Test @@ -393,6 +431,95 @@ public class WifiDetailPreferenceControllerTest { verify(mockDnsPref, never()).setVisible(true); } + // Convenience method to convert a LinkAddress to a string without a prefix length. + private String asString(LinkAddress l) { + return l.getAddress().getHostAddress(); + } + + // Pretend that the NetworkCallback was triggered with a new copy of lp. We need to create a + // new copy because the code only updates if !mLinkProperties.equals(lp). + private void updateLinkProperties(LinkProperties lp) { + mCallbackCaptor.getValue().onLinkPropertiesChanged(mockNetwork, new LinkProperties(lp)); + } + + // Check that all IP information is deleted before being redrawn. + // TODO: switch the code to redrawing in place and remove this method. + private void verifyIpLayerInfoCleared(InOrder inOrder) { + inOrder.verify(mockIpv6AddressCategory).removeAll(); + inOrder.verify(mockIpv6AddressCategory).setVisible(false); + inOrder.verify(mockIpAddressPref).setVisible(false); + inOrder.verify(mockSubnetPref).setVisible(false); + inOrder.verify(mockGatewayPref).setVisible(false); + inOrder.verify(mockDnsPref).setVisible(false); + } + + private void verifyDisplayedIpv6Addresses(InOrder inOrder, LinkAddress... addresses) { + for (LinkAddress l: addresses) { + inOrder.verify(mockIpv6AddressCategory).addPreference(mIpv6AddressCaptor.capture()); + assertThat(mIpv6AddressCaptor.getValue().getTitle()).isEqualTo(asString(l)); + } + inOrder.verify(mockIpv6AddressCategory).setVisible(true); + } + + @Test + public void onLinkPropertiesChanged_updatesFields() { + mController.displayPreference(mockScreen); + mController.onResume(); + + InOrder inOrder = inOrder(mockIpAddressPref, mockGatewayPref, mockSubnetPref, + mockDnsPref, mockIpv6AddressCategory); + + LinkProperties lp = new LinkProperties(); + + lp.addLinkAddress(Constants.IPV6_LINKLOCAL); + updateLinkProperties(lp); + verifyIpLayerInfoCleared(inOrder); + verifyDisplayedIpv6Addresses(inOrder, Constants.IPV6_LINKLOCAL); + + lp.addRoute(Constants.IPV4_DEFAULT); + updateLinkProperties(lp); + verifyIpLayerInfoCleared(inOrder); + inOrder.verify(mockGatewayPref).setDetailText(Constants.IPV4_GATEWAY.getHostAddress()); + inOrder.verify(mockGatewayPref).setVisible(true); + + lp.addLinkAddress(Constants.IPV4_ADDR); + lp.addRoute(Constants.IPV4_SUBNET); + updateLinkProperties(lp); + verifyIpLayerInfoCleared(inOrder); + inOrder.verify(mockIpAddressPref).setDetailText(asString(Constants.IPV4_ADDR)); + inOrder.verify(mockIpAddressPref).setVisible(true); + inOrder.verify(mockSubnetPref).setDetailText("255.255.255.128"); + inOrder.verify(mockSubnetPref).setVisible(true); + + lp.addLinkAddress(Constants.IPV6_GLOBAL1); + lp.addLinkAddress(Constants.IPV6_GLOBAL2); + updateLinkProperties(lp); + verifyIpLayerInfoCleared(inOrder); + verifyDisplayedIpv6Addresses(inOrder, + Constants.IPV6_LINKLOCAL, + Constants.IPV6_GLOBAL1, + Constants.IPV6_GLOBAL2); + + lp.removeLinkAddress(Constants.IPV6_GLOBAL1); + updateLinkProperties(lp); + verifyIpLayerInfoCleared(inOrder); + verifyDisplayedIpv6Addresses(inOrder, + Constants.IPV6_LINKLOCAL, + Constants.IPV6_GLOBAL2); + + lp.addDnsServer(Constants.IPV6_DNS); + updateLinkProperties(lp); + inOrder.verify(mockDnsPref, never()).setVisible(true); + + lp.addDnsServer(Constants.IPV4_DNS1); + lp.addDnsServer(Constants.IPV4_DNS2); + updateLinkProperties(lp); + inOrder.verify(mockDnsPref).setDetailText( + Constants.IPV4_DNS1.getHostAddress() + "," + + Constants.IPV4_DNS2.getHostAddress()); + inOrder.verify(mockDnsPref).setVisible(true); + } + @Test public void canForgetNetwork_noNetwork() { when(mockAccessPoint.getConfig()).thenReturn(null); @@ -496,28 +623,27 @@ public class WifiDetailPreferenceControllerTest { @Test public void ipv6AddressPref_shouldHaveHostAddressTextSet() { - LinkAddress ipv6Address = new LinkAddress(mIpv6Address, 128); - - mLinkProperties.addLinkAddress(ipv6Address); + mLinkProperties.addLinkAddress(Constants.IPV6_LINKLOCAL); + mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL1); + mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2); mController.displayPreference(mockScreen); - ArgumentCaptor preferenceCaptor = ArgumentCaptor.forClass(Preference.class); - verify(mockIpv6AddressCategory).addPreference(preferenceCaptor.capture()); - assertThat(preferenceCaptor.getValue().getTitle()).isEqualTo(mIpv6Address.getHostAddress()); + List addrs = mIpv6AddressCaptor.getAllValues(); + assertThat(addrs.size()).isEqualTo(3); + + assertThat((String) addrs.get(0).getTitle()).isEqualTo(asString(Constants.IPV6_LINKLOCAL)); + assertThat((String) addrs.get(1).getTitle()).isEqualTo(asString(Constants.IPV6_GLOBAL1)); + assertThat((String) addrs.get(2).getTitle()).isEqualTo(asString(Constants.IPV6_GLOBAL2)); } @Test public void ipv6AddressPref_shouldNotBeSelectable() { - LinkAddress ipv6Address = new LinkAddress(mIpv6Address, 128); - - mLinkProperties.addLinkAddress(ipv6Address); + mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2); mController.displayPreference(mockScreen); - ArgumentCaptor preferenceCaptor = ArgumentCaptor.forClass(Preference.class); - verify(mockIpv6AddressCategory).addPreference(preferenceCaptor.capture()); - assertThat(preferenceCaptor.getValue().isSelectable()).isFalse(); + assertThat(mockIpv6AddressCategory.isSelectable()).isFalse(); } @Test From 0bde06cd594840fb4aa49fe1b034a3859f36c246 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 2 May 2017 12:06:32 +0900 Subject: [PATCH 2/2] Reduce jank in the wifi detail status page. Currently, when anything changes, the wifi detail status page removes and then redraws all IP address information. This causes the whole screen to flicker. Instead, only add and remove things when they actually change. In order to do this, convert the IPv6 addresses from a list of Preference objects to a single newline-separated text field. This removes the need to keep track of addresses as they are added and deleted, and also looks a bit better. Also, minor correctness fixes: - Get the gateway from the default route, not from the last route with a non-null gateway. - Get the IPv4 subnet mask from the IPv4 address prefix, not from the last route with prefix length > 0. Bug: 62171690 Test: make -j64 RunSettingsRoboTests Test: IP information does not flicker when signal strength changes Change-Id: Ia9f2a277e53a2800407ae327701c5b95a9eec20a --- res/xml/wifi_network_details_fragment.xml | 8 +- .../settings/wifi/WifiDetailPreference.java | 2 + .../WifiDetailPreferenceController.java | 116 +++++++++--------- .../WifiDetailPreferenceControllerTest.java | 59 ++++----- 4 files changed, 89 insertions(+), 96 deletions(-) diff --git a/res/xml/wifi_network_details_fragment.xml b/res/xml/wifi_network_details_fragment.xml index 5d5958d534e..257533f0cc9 100644 --- a/res/xml/wifi_network_details_fragment.xml +++ b/res/xml/wifi_network_details_fragment.xml @@ -82,8 +82,12 @@ + android:selectable="false"> + + diff --git a/src/com/android/settings/wifi/WifiDetailPreference.java b/src/com/android/settings/wifi/WifiDetailPreference.java index 6d34ad1c68f..b62df56671f 100644 --- a/src/com/android/settings/wifi/WifiDetailPreference.java +++ b/src/com/android/settings/wifi/WifiDetailPreference.java @@ -19,6 +19,7 @@ package com.android.settings.wifi; import android.content.Context; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceViewHolder; +import android.text.TextUtils; import android.util.AttributeSet; import android.widget.TextView; @@ -37,6 +38,7 @@ public class WifiDetailPreference extends Preference { } public void setDetailText(String text) { + if (TextUtils.equals(mDetailText, text)) return; mDetailText = text; notifyChanged(); } diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java index a5d922da82f..b22d7027dd6 100644 --- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java +++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java @@ -27,6 +27,7 @@ import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.IpPrefix; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkBadging; @@ -67,6 +68,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; import java.util.StringJoiner; +import java.util.stream.Collectors; /** * Controller for logic pertaining to displaying Wifi information for the @@ -100,7 +102,9 @@ public class WifiDetailPreferenceController extends PreferenceController impleme @VisibleForTesting static final String KEY_DNS_PREF = "dns"; @VisibleForTesting - static final String KEY_IPV6_ADDRESS_CATEGORY = "ipv6_details_category"; + static final String KEY_IPV6_CATEGORY = "ipv6_category"; + @VisibleForTesting + static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses"; private AccessPoint mAccessPoint; private final ConnectivityManagerWrapper mConnectivityManagerWrapper; @@ -133,8 +137,9 @@ public class WifiDetailPreferenceController extends PreferenceController impleme private WifiDetailPreference mGatewayPref; private WifiDetailPreference mSubnetPref; private WifiDetailPreference mDnsPref; + private PreferenceCategory mIpv6Category; + private Preference mIpv6AddressPref; - private PreferenceCategory mIpv6AddressCategory; private final IntentFilter mFilter; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -241,8 +246,8 @@ public class WifiDetailPreferenceController extends PreferenceController impleme mSubnetPref = (WifiDetailPreference) screen.findPreference(KEY_SUBNET_MASK_PREF); mDnsPref = (WifiDetailPreference) screen.findPreference(KEY_DNS_PREF); - mIpv6AddressCategory = - (PreferenceCategory) screen.findPreference(KEY_IPV6_ADDRESS_CATEGORY); + mIpv6Category = (PreferenceCategory) screen.findPreference(KEY_IPV6_CATEGORY); + mIpv6AddressPref = (Preference) screen.findPreference(KEY_IPV6_ADDRESSES_PREF); mSecurityPref.setDetailText(mAccessPoint.getSecurityString(false /* concise */)); mForgetButton = (Button) mButtonsPref.findViewById(R.id.left_button); @@ -315,8 +320,6 @@ public class WifiDetailPreferenceController extends PreferenceController impleme mFrequencyPref.setDetailText(band); updateIpLayerInfo(); - mButtonsPref.setVisible(mForgetButton.getVisibility() == View.VISIBLE - || mSignInButton.getVisibility() == View.VISIBLE); } private void exitActivity() { @@ -348,74 +351,69 @@ public class WifiDetailPreferenceController extends PreferenceController impleme mSignalStrengthPref.setDetailText(mSignalStr[summarySignalLevel]); } + private void updatePreference(WifiDetailPreference pref, String detailText) { + if (!TextUtils.isEmpty(detailText)) { + pref.setDetailText(detailText); + pref.setVisible(true); + } else { + pref.setVisible(false); + } + } + private void updateIpLayerInfo() { mSignInButton.setVisibility(canSignIntoNetwork() ? View.VISIBLE : View.INVISIBLE); - - // Reset all fields - mIpv6AddressCategory.removeAll(); - mIpv6AddressCategory.setVisible(false); - mIpAddressPref.setVisible(false); - mSubnetPref.setVisible(false); - mGatewayPref.setVisible(false); - mDnsPref.setVisible(false); + mButtonsPref.setVisible(mForgetButton.getVisibility() == View.VISIBLE + || mSignInButton.getVisibility() == View.VISIBLE); if (mNetwork == null || mLinkProperties == null) { + mIpAddressPref.setVisible(false); + mSubnetPref.setVisible(false); + mGatewayPref.setVisible(false); + mDnsPref.setVisible(false); + mIpv6Category.setVisible(false); return; } - List addresses = mLinkProperties.getAddresses(); - // Set IPv4 and IPv6 addresses - for (int i = 0; i < addresses.size(); i++) { - InetAddress addr = addresses.get(i); - if (addr instanceof Inet4Address) { - mIpAddressPref.setDetailText(addr.getHostAddress()); - mIpAddressPref.setVisible(true); - } else if (addr instanceof Inet6Address) { - String ip = addr.getHostAddress(); - Preference pref = new Preference(mPrefContext); - pref.setKey(ip); - pref.setTitle(ip); - pref.setSelectable(false); - mIpv6AddressCategory.addPreference(pref); - mIpv6AddressCategory.setVisible(true); - } - } - - // Set up IPv4 gateway and subnet mask - String gateway = null; + // Find IPv4 and IPv6 addresses. + String ipv4Address = null; String subnet = null; + StringJoiner ipv6Addresses = new StringJoiner("\n"); + + for (LinkAddress addr : mLinkProperties.getLinkAddresses()) { + if (addr.getAddress() instanceof Inet4Address) { + ipv4Address = addr.getAddress().getHostAddress(); + subnet = ipv4PrefixLengthToSubnetMask(addr.getPrefixLength()); + } else if (addr.getAddress() instanceof Inet6Address) { + ipv6Addresses.add(addr.getAddress().getHostAddress()); + } + } + + // Find IPv4 default gateway. + String gateway = null; for (RouteInfo routeInfo : mLinkProperties.getRoutes()) { - if (routeInfo.hasGateway() && routeInfo.getGateway() instanceof Inet4Address) { + if (routeInfo.isIPv4Default() && routeInfo.hasGateway()) { gateway = routeInfo.getGateway().getHostAddress(); - } - IpPrefix ipPrefix = routeInfo.getDestination(); - if (ipPrefix != null && ipPrefix.getAddress() instanceof Inet4Address - && ipPrefix.getPrefixLength() > 0) { - subnet = ipv4PrefixLengthToSubnetMask(ipPrefix.getPrefixLength()); + break; } } - if (!TextUtils.isEmpty(subnet)) { - mSubnetPref.setDetailText(subnet); - mSubnetPref.setVisible(true); - } + // Find IPv4 DNS addresses. + String dnsServers = mLinkProperties.getDnsServers().stream() + .filter(Inet4Address.class::isInstance) + .map(InetAddress::getHostAddress) + .collect(Collectors.joining(",")); - if (!TextUtils.isEmpty(gateway)) { - mGatewayPref.setDetailText(gateway); - mGatewayPref.setVisible(true); - } + // Update UI. + updatePreference(mIpAddressPref, ipv4Address); + updatePreference(mSubnetPref, subnet); + updatePreference(mGatewayPref, gateway); + updatePreference(mDnsPref, dnsServers); - // Set IPv4 DNS addresses - StringJoiner stringJoiner = new StringJoiner(","); - for (InetAddress dnsServer : mLinkProperties.getDnsServers()) { - if (dnsServer instanceof Inet4Address) { - stringJoiner.add(dnsServer.getHostAddress()); - } - } - String dnsText = stringJoiner.toString(); - if (!dnsText.isEmpty()) { - mDnsPref.setDetailText(dnsText); - mDnsPref.setVisible(true); + if (ipv6Addresses.length() > 0) { + mIpv6AddressPref.setSummary(ipv6Addresses.toString()); + mIpv6Category.setVisible(true); + } else { + mIpv6Category.setVisible(false); } } diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java index 14556185089..17801b1bc85 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java @@ -79,7 +79,9 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @@ -119,7 +121,8 @@ public class WifiDetailPreferenceControllerTest { @Mock private WifiDetailPreference mockSubnetPref; @Mock private WifiDetailPreference mockDnsPref; @Mock private Button mockForgetButton; - @Mock private PreferenceCategory mockIpv6AddressCategory; + @Mock private PreferenceCategory mockIpv6Category; + @Mock private WifiDetailPreference mockIpv6AddressesPref; @Captor private ArgumentCaptor mCallbackCaptor; @Captor private ArgumentCaptor mForgetClickListener; @@ -211,8 +214,6 @@ public class WifiDetailPreferenceControllerTest { when(mockFragment.getActivity()).thenReturn(mockActivity); - when(mockIpv6AddressCategory.addPreference(mIpv6AddressCaptor.capture())).thenReturn(true); - setupMockedPreferenceScreen(); mController = newWifiDetailPreferenceController(); } @@ -258,8 +259,10 @@ public class WifiDetailPreferenceControllerTest { .thenReturn(mockSubnetPref); when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_DNS_PREF)) .thenReturn(mockDnsPref); - when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_ADDRESS_CATEGORY)) - .thenReturn(mockIpv6AddressCategory); + when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_CATEGORY)) + .thenReturn(mockIpv6Category); + when(mockScreen.findPreference(WifiDetailPreferenceController.KEY_IPV6_ADDRESSES_PREF)) + .thenReturn(mockIpv6AddressesPref); } @Test @@ -414,17 +417,17 @@ public class WifiDetailPreferenceControllerTest { @Test public void noLinkProperties_allIpDetailsHidden() { when(mockConnectivityManager.getLinkProperties(mockNetwork)).thenReturn(null); - reset(mockIpv6AddressCategory, mockIpAddressPref, mockSubnetPref, mockGatewayPref, + reset(mockIpv6Category, mockIpAddressPref, mockSubnetPref, mockGatewayPref, mockDnsPref); mController.displayPreference(mockScreen); - verify(mockIpv6AddressCategory).setVisible(false); + verify(mockIpv6Category).setVisible(false); verify(mockIpAddressPref).setVisible(false); verify(mockSubnetPref).setVisible(false); verify(mockGatewayPref).setVisible(false); verify(mockDnsPref).setVisible(false); - verify(mockIpv6AddressCategory, never()).setVisible(true); + verify(mockIpv6Category, never()).setVisible(true); verify(mockIpAddressPref, never()).setVisible(true); verify(mockSubnetPref, never()).setVisible(true); verify(mockGatewayPref, never()).setVisible(true); @@ -442,23 +445,11 @@ public class WifiDetailPreferenceControllerTest { mCallbackCaptor.getValue().onLinkPropertiesChanged(mockNetwork, new LinkProperties(lp)); } - // Check that all IP information is deleted before being redrawn. - // TODO: switch the code to redrawing in place and remove this method. - private void verifyIpLayerInfoCleared(InOrder inOrder) { - inOrder.verify(mockIpv6AddressCategory).removeAll(); - inOrder.verify(mockIpv6AddressCategory).setVisible(false); - inOrder.verify(mockIpAddressPref).setVisible(false); - inOrder.verify(mockSubnetPref).setVisible(false); - inOrder.verify(mockGatewayPref).setVisible(false); - inOrder.verify(mockDnsPref).setVisible(false); - } - private void verifyDisplayedIpv6Addresses(InOrder inOrder, LinkAddress... addresses) { - for (LinkAddress l: addresses) { - inOrder.verify(mockIpv6AddressCategory).addPreference(mIpv6AddressCaptor.capture()); - assertThat(mIpv6AddressCaptor.getValue().getTitle()).isEqualTo(asString(l)); - } - inOrder.verify(mockIpv6AddressCategory).setVisible(true); + String text = Arrays.stream(addresses) + .map(address -> asString(address)) + .collect(Collectors.joining("\n")); + inOrder.verify(mockIpv6AddressesPref).setSummary(text); } @Test @@ -467,25 +458,23 @@ public class WifiDetailPreferenceControllerTest { mController.onResume(); InOrder inOrder = inOrder(mockIpAddressPref, mockGatewayPref, mockSubnetPref, - mockDnsPref, mockIpv6AddressCategory); + mockDnsPref, mockIpv6Category, mockIpv6AddressesPref); LinkProperties lp = new LinkProperties(); lp.addLinkAddress(Constants.IPV6_LINKLOCAL); updateLinkProperties(lp); - verifyIpLayerInfoCleared(inOrder); verifyDisplayedIpv6Addresses(inOrder, Constants.IPV6_LINKLOCAL); + inOrder.verify(mockIpv6Category).setVisible(true); lp.addRoute(Constants.IPV4_DEFAULT); updateLinkProperties(lp); - verifyIpLayerInfoCleared(inOrder); inOrder.verify(mockGatewayPref).setDetailText(Constants.IPV4_GATEWAY.getHostAddress()); inOrder.verify(mockGatewayPref).setVisible(true); lp.addLinkAddress(Constants.IPV4_ADDR); lp.addRoute(Constants.IPV4_SUBNET); updateLinkProperties(lp); - verifyIpLayerInfoCleared(inOrder); inOrder.verify(mockIpAddressPref).setDetailText(asString(Constants.IPV4_ADDR)); inOrder.verify(mockIpAddressPref).setVisible(true); inOrder.verify(mockSubnetPref).setDetailText("255.255.255.128"); @@ -494,7 +483,6 @@ public class WifiDetailPreferenceControllerTest { lp.addLinkAddress(Constants.IPV6_GLOBAL1); lp.addLinkAddress(Constants.IPV6_GLOBAL2); updateLinkProperties(lp); - verifyIpLayerInfoCleared(inOrder); verifyDisplayedIpv6Addresses(inOrder, Constants.IPV6_LINKLOCAL, Constants.IPV6_GLOBAL1, @@ -502,7 +490,6 @@ public class WifiDetailPreferenceControllerTest { lp.removeLinkAddress(Constants.IPV6_GLOBAL1); updateLinkProperties(lp); - verifyIpLayerInfoCleared(inOrder); verifyDisplayedIpv6Addresses(inOrder, Constants.IPV6_LINKLOCAL, Constants.IPV6_GLOBAL2); @@ -630,11 +617,13 @@ public class WifiDetailPreferenceControllerTest { mController.displayPreference(mockScreen); List addrs = mIpv6AddressCaptor.getAllValues(); - assertThat(addrs.size()).isEqualTo(3); - assertThat((String) addrs.get(0).getTitle()).isEqualTo(asString(Constants.IPV6_LINKLOCAL)); - assertThat((String) addrs.get(1).getTitle()).isEqualTo(asString(Constants.IPV6_GLOBAL1)); - assertThat((String) addrs.get(2).getTitle()).isEqualTo(asString(Constants.IPV6_GLOBAL2)); + String expectedAddresses = String.join("\n", + asString(Constants.IPV6_LINKLOCAL), + asString(Constants.IPV6_GLOBAL1), + asString(Constants.IPV6_GLOBAL2)); + + verify(mockIpv6AddressesPref).setSummary(expectedAddresses); } @Test @@ -643,7 +632,7 @@ public class WifiDetailPreferenceControllerTest { mController.displayPreference(mockScreen); - assertThat(mockIpv6AddressCategory.isSelectable()).isFalse(); + assertThat(mockIpv6AddressesPref.isSelectable()).isFalse(); } @Test