1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.net.module.util;
18 
19 import static com.android.testutils.MiscAsserts.assertSameElements;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.annotation.SuppressLint;
26 import android.net.InetAddresses;
27 import android.net.IpPrefix;
28 import android.net.LinkAddress;
29 import android.net.LinkProperties;
30 import android.net.ProxyInfo;
31 import android.net.RouteInfo;
32 import android.util.ArraySet;
33 
34 import androidx.test.runner.AndroidJUnit4;
35 
36 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
37 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
38 
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 
42 import java.net.InetAddress;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.List;
46 import java.util.function.Function;
47 
48 @RunWith(AndroidJUnit4.class)
49 public final class LinkPropertiesUtilsTest {
50     @SuppressLint("NewApi")
51     private static final IpPrefix PREFIX = new IpPrefix(toInetAddress("75.208.6.0"), 24);
52     private static final InetAddress V4_ADDR = toInetAddress("75.208.6.1");
53     private static final InetAddress V6_ADDR  = toInetAddress(
54             "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
55     private static final InetAddress DNS1 = toInetAddress("75.208.7.1");
56     private static final InetAddress DNS2 = toInetAddress("69.78.7.1");
57 
58     private static final InetAddress GATEWAY1 = toInetAddress("75.208.8.1");
59     private static final InetAddress GATEWAY2 = toInetAddress("69.78.8.1");
60 
61     private static final String IF_NAME = "wlan0";
62     private static final LinkAddress V4_LINKADDR = new LinkAddress(V4_ADDR, 32);
63     private static final LinkAddress V6_LINKADDR = new LinkAddress(V6_ADDR, 128);
64     private static final RouteInfo RT_INFO1 = new RouteInfo(PREFIX, GATEWAY1, IF_NAME);
65     private static final RouteInfo RT_INFO2 = new RouteInfo(PREFIX, GATEWAY2, IF_NAME);
66     private static final String TEST_DOMAIN = "link.properties.com";
67 
toInetAddress(String addrString)68     private static InetAddress toInetAddress(String addrString) {
69         return InetAddresses.parseNumericAddress(addrString);
70     }
71 
createTestObject()72     private LinkProperties createTestObject() {
73         final LinkProperties lp = new LinkProperties();
74         lp.setInterfaceName(IF_NAME);
75         lp.addLinkAddress(V4_LINKADDR);
76         lp.addLinkAddress(V6_LINKADDR);
77         lp.addDnsServer(DNS1);
78         lp.addDnsServer(DNS2);
79         lp.setDomains(TEST_DOMAIN);
80         lp.addRoute(RT_INFO1);
81         lp.addRoute(RT_INFO2);
82         lp.setHttpProxy(ProxyInfo.buildDirectProxy("test", 8888));
83         return lp;
84     }
85 
86     @Test
testLinkPropertiesIdenticalEqual()87     public void testLinkPropertiesIdenticalEqual() {
88         final LinkProperties source = createTestObject();
89         final LinkProperties target = new LinkProperties(source);
90 
91         assertTrue(LinkPropertiesUtils.isIdenticalInterfaceName(source, target));
92         assertTrue(LinkPropertiesUtils.isIdenticalInterfaceName(target, source));
93 
94         assertTrue(LinkPropertiesUtils.isIdenticalAddresses(source, target));
95         assertTrue(LinkPropertiesUtils.isIdenticalAddresses(target, source));
96 
97         assertTrue(LinkPropertiesUtils.isIdenticalAllLinkAddresses(source, target));
98         assertTrue(LinkPropertiesUtils.isIdenticalAllLinkAddresses(target, source));
99 
100         assertTrue(LinkPropertiesUtils.isIdenticalDnses(source, target));
101         assertTrue(LinkPropertiesUtils.isIdenticalDnses(target, source));
102 
103         assertTrue(LinkPropertiesUtils.isIdenticalRoutes(source, target));
104         assertTrue(LinkPropertiesUtils.isIdenticalRoutes(target, source));
105 
106         assertTrue(LinkPropertiesUtils.isIdenticalHttpProxy(source, target));
107         assertTrue(LinkPropertiesUtils.isIdenticalHttpProxy(target, source));
108 
109         // Test different interface name.
110         target.setInterfaceName("lo");
111         assertFalse(LinkPropertiesUtils.isIdenticalInterfaceName(source, target));
112         assertFalse(LinkPropertiesUtils.isIdenticalInterfaceName(target, source));
113         // Restore interface name
114         target.setInterfaceName(IF_NAME);
115 
116         // Compare addresses.size() not equals.
117         final LinkAddress testLinkAddr = new LinkAddress(toInetAddress("75.208.6.2"), 32);
118         target.addLinkAddress(testLinkAddr);
119         assertFalse(LinkPropertiesUtils.isIdenticalAddresses(source, target));
120         assertFalse(LinkPropertiesUtils.isIdenticalAddresses(target, source));
121 
122         assertFalse(LinkPropertiesUtils.isIdenticalAllLinkAddresses(source, target));
123         assertFalse(LinkPropertiesUtils.isIdenticalAllLinkAddresses(target, source));
124 
125         // Currently, target contains V4_LINKADDR, V6_LINKADDR and testLinkAddr.
126         // Compare addresses.size() equals but contains different address.
127         target.removeLinkAddress(V4_LINKADDR);
128         assertEquals(source.getAddresses().size(), target.getAddresses().size());
129         assertFalse(LinkPropertiesUtils.isIdenticalAddresses(source, target));
130         assertFalse(LinkPropertiesUtils.isIdenticalAddresses(target, source));
131         assertFalse(LinkPropertiesUtils.isIdenticalAllLinkAddresses(source, target));
132         assertFalse(LinkPropertiesUtils.isIdenticalAllLinkAddresses(target, source));
133         // Restore link address
134         target.addLinkAddress(V4_LINKADDR);
135         target.removeLinkAddress(testLinkAddr);
136 
137         // Compare size not equals.
138         target.addDnsServer(toInetAddress("75.208.10.1"));
139         assertFalse(LinkPropertiesUtils.isIdenticalDnses(source, target));
140         assertFalse(LinkPropertiesUtils.isIdenticalDnses(target, source));
141 
142         // Compare the same servers but target has different domains.
143         target.removeDnsServer(toInetAddress("75.208.10.1"));
144         target.setDomains("test.com");
145         assertFalse(LinkPropertiesUtils.isIdenticalDnses(source, target));
146         assertFalse(LinkPropertiesUtils.isIdenticalDnses(target, source));
147 
148         // Test null domain.
149         target.setDomains(null);
150         assertFalse(LinkPropertiesUtils.isIdenticalDnses(source, target));
151         assertFalse(LinkPropertiesUtils.isIdenticalDnses(target, source));
152         // Restore domain
153         target.setDomains(TEST_DOMAIN);
154 
155         // Compare size not equals.
156         final RouteInfo testRoute = new RouteInfo(toInetAddress("75.208.7.1"));
157         target.addRoute(testRoute);
158         assertFalse(LinkPropertiesUtils.isIdenticalRoutes(source, target));
159         assertFalse(LinkPropertiesUtils.isIdenticalRoutes(target, source));
160 
161         // Currently, target contains RT_INFO1, RT_INFO2 and testRoute.
162         // Compare size equals but different routes.
163         target.removeRoute(RT_INFO1);
164         assertEquals(source.getRoutes().size(), target.getRoutes().size());
165         assertFalse(LinkPropertiesUtils.isIdenticalRoutes(source, target));
166         assertFalse(LinkPropertiesUtils.isIdenticalRoutes(target, source));
167         // Restore route
168         target.addRoute(RT_INFO1);
169         target.removeRoute(testRoute);
170 
171         // Test different proxy.
172         target.setHttpProxy(ProxyInfo.buildDirectProxy("hello", 8888));
173         assertFalse(LinkPropertiesUtils.isIdenticalHttpProxy(source, target));
174         assertFalse(LinkPropertiesUtils.isIdenticalHttpProxy(target, source));
175 
176         // Test null proxy.
177         target.setHttpProxy(null);
178         assertFalse(LinkPropertiesUtils.isIdenticalHttpProxy(source, target));
179         assertFalse(LinkPropertiesUtils.isIdenticalHttpProxy(target, source));
180 
181         final LinkProperties stacked = new LinkProperties();
182         stacked.setInterfaceName("v4-" + target.getInterfaceName());
183         stacked.addLinkAddress(testLinkAddr);
184         target.addStackedLink(stacked);
185         assertFalse(LinkPropertiesUtils.isIdenticalAllLinkAddresses(source, target));
186         assertFalse(LinkPropertiesUtils.isIdenticalAllLinkAddresses(target, source));
187     }
188 
compareResult(List<T> oldItems, List<T> newItems, List<T> expectRemoved, List<T> expectAdded)189     private <T> void compareResult(List<T> oldItems, List<T> newItems, List<T> expectRemoved,
190             List<T> expectAdded) {
191         CompareResult<T> result = new CompareResult<>(oldItems, newItems);
192         assertEquals(new ArraySet<>(expectAdded), new ArraySet<>(result.added));
193         assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
194     }
195 
196     @Test
testCompareResult()197     public void testCompareResult() {
198         // Either adding or removing items
199         compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1),
200                 Arrays.asList(2, 3, 4), new ArrayList<>());
201         compareResult(Arrays.asList(1, 2), Arrays.asList(3, 2, 1, 4),
202                 new ArrayList<>(), Arrays.asList(3, 4));
203 
204 
205         // adding and removing items at the same time
206         compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(2, 3, 4, 5),
207                 Arrays.asList(1), Arrays.asList(5));
208         compareResult(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6),
209                 Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
210 
211         // null cases
212         compareResult(Arrays.asList(1, 2, 3), null, Arrays.asList(1, 2, 3), new ArrayList<>());
213         compareResult(null, Arrays.asList(3, 2, 1), new ArrayList<>(), Arrays.asList(1, 2, 3));
214         compareResult(null, null, new ArrayList<>(), new ArrayList<>());
215 
216         // Some more tests with strings
217         final ArrayList<String> list1 = new ArrayList<>();
218         list1.add("string1");
219 
220         final ArrayList<String> list2 = new ArrayList<>(list1);
221         final CompareResult<String> cr1 = new CompareResult<>(list1, list2);
222         assertTrue(cr1.added.isEmpty());
223         assertTrue(cr1.removed.isEmpty());
224 
225         list2.add("string2");
226         final CompareResult<String> cr2 = new CompareResult<>(list1, list2);
227         assertEquals(Arrays.asList("string2"), cr2.added);
228         assertTrue(cr2.removed.isEmpty());
229 
230         list2.remove("string1");
231         final CompareResult<String> cr3 = new CompareResult<>(list1, list2);
232         assertEquals(Arrays.asList("string2"), cr3.added);
233         assertEquals(Arrays.asList("string1"), cr3.removed);
234 
235         list1.add("string2");
236         final CompareResult<String> cr4 = new CompareResult<>(list1, list2);
237         assertTrue(cr4.added.isEmpty());
238         assertEquals(Arrays.asList("string1"), cr3.removed);
239     }
240 
241     @Test
testCompareAddresses()242     public void testCompareAddresses() {
243         final LinkProperties source = createTestObject();
244         final LinkProperties target = new LinkProperties(source);
245         final InetAddress addr1 = toInetAddress("75.208.6.2");
246         final LinkAddress linkAddr1 = new LinkAddress(addr1, 32);
247 
248         CompareResult<LinkAddress> results = LinkPropertiesUtils.compareAddresses(source, target);
249         assertEquals(0, results.removed.size());
250         assertEquals(0, results.added.size());
251 
252         source.addLinkAddress(linkAddr1);
253         results = LinkPropertiesUtils.compareAddresses(source, target);
254         assertEquals(1, results.removed.size());
255         assertEquals(linkAddr1, results.removed.get(0));
256         assertEquals(0, results.added.size());
257 
258         final InetAddress addr2 = toInetAddress("75.208.6.3");
259         final LinkAddress linkAddr2 = new LinkAddress(addr2, 32);
260 
261         target.addLinkAddress(linkAddr2);
262         results = LinkPropertiesUtils.compareAddresses(source, target);
263         assertEquals(linkAddr1, results.removed.get(0));
264         assertEquals(linkAddr2, results.added.get(0));
265     }
266 
assertCompareOrUpdateResult(CompareOrUpdateResult result, List<String> expectedAdded, List<String> expectedRemoved, List<String> expectedUpdated)267     private void assertCompareOrUpdateResult(CompareOrUpdateResult result,
268             List<String> expectedAdded, List<String> expectedRemoved,
269             List<String> expectedUpdated) {
270         assertSameElements(expectedAdded, result.added);
271         assertSameElements(expectedRemoved, result.removed);
272         assertSameElements(expectedUpdated, result.updated);
273     }
274 
strArray(String... strs)275     private List<String> strArray(String... strs) {
276         return Arrays.asList(strs);
277     }
278 
279     @Test
testCompareOrUpdateResult()280     public void testCompareOrUpdateResult() {
281         // As the item type, use a simple string. An item is defined to be an update of another item
282         // if the string starts with the same alphabetical characters.
283         // Extracting the key from the object is just a regexp.
284         Function<String, String> extractPrefix = (s) -> s.replaceFirst("^([a-z]+).*", "$1");
285         assertEquals("goodbye", extractPrefix.apply("goodbye1234"));
286 
287         List<String> oldItems = strArray("hello123", "goodbye5678", "howareyou669");
288         List<String> newItems = strArray("hello123", "goodbye000", "verywell");
289 
290         final List<String> emptyList = new ArrayList<>();
291 
292         // Items -> empty: everything removed.
293         CompareOrUpdateResult<String, String> result =
294                 new CompareOrUpdateResult<String, String>(oldItems, emptyList, extractPrefix);
295         assertCompareOrUpdateResult(result,
296                 emptyList, strArray("hello123", "howareyou669", "goodbye5678"), emptyList);
297 
298         // Empty -> items: everything added.
299         result = new CompareOrUpdateResult<String, String>(emptyList, newItems, extractPrefix);
300         assertCompareOrUpdateResult(result,
301                 strArray("hello123", "goodbye000", "verywell"), emptyList,  emptyList);
302 
303         // Empty -> empty: no change.
304         result = new CompareOrUpdateResult<String, String>(newItems, newItems, extractPrefix);
305         assertCompareOrUpdateResult(result,  emptyList,  emptyList, emptyList);
306 
307         // Added, removed, updated at the same time.
308         result =  new CompareOrUpdateResult<>(oldItems, newItems, extractPrefix);
309         assertCompareOrUpdateResult(result,
310                 strArray("verywell"), strArray("howareyou669"), strArray("goodbye000"));
311 
312         // Null -> items: everything added.
313         result = new CompareOrUpdateResult<String, String>(null, newItems, extractPrefix);
314         assertCompareOrUpdateResult(result,
315                 strArray("hello123", "goodbye000", "verywell"), emptyList,  emptyList);
316 
317         // Items -> null: everything removed.
318         result = new CompareOrUpdateResult<String, String>(oldItems, null, extractPrefix);
319         assertCompareOrUpdateResult(result,
320                 emptyList, strArray("hello123", "howareyou669", "goodbye5678"), emptyList);
321 
322         // Null -> null: all lists empty.
323         result = new CompareOrUpdateResult<String, String>(null, null, extractPrefix);
324         assertCompareOrUpdateResult(result, emptyList, emptyList, emptyList);
325     }
326 }
327