Move DhcpServer to NetworkStack app
Test: atest FrameworksNetTests && atest NetworkStackTests
Bug: b/112869080
Change-Id: I96c40e63e9ceb37b67705bdd4d120307e114715b
diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
new file mode 100644
index 0000000..51d50d9
--- /dev/null
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -0,0 +1,539 @@
+/*
+ * 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 android.net.dhcp;
+
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
+import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
+import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+
+import static java.lang.String.format;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.IpPrefix;
+import android.net.MacAddress;
+import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.net.Inet4Address;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DhcpLeaseRepositoryTest {
+ private static final Inet4Address INET4_ANY = (Inet4Address) Inet4Address.ANY;
+ private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247");
+ private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241");
+ private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243");
+ private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes(
+ new byte[] { 5, 4, 3, 2, 1, 0 });
+ private static final MacAddress TEST_MAC_2 = MacAddress.fromBytes(
+ new byte[] { 0, 1, 2, 3, 4, 5 });
+ private static final MacAddress TEST_MAC_3 = MacAddress.fromBytes(
+ new byte[] { 0, 1, 2, 3, 4, 6 });
+ private static final Inet4Address TEST_INETADDR_1 = parseAddr4("192.168.42.248");
+ private static final Inet4Address TEST_INETADDR_2 = parseAddr4("192.168.42.249");
+ private static final String TEST_HOSTNAME_1 = "hostname1";
+ private static final String TEST_HOSTNAME_2 = "hostname2";
+ private static final IpPrefix TEST_IP_PREFIX = new IpPrefix(TEST_SERVER_ADDR, 22);
+ private static final long TEST_TIME = 100L;
+ private static final int TEST_LEASE_TIME_MS = 3_600_000;
+ private static final Set<Inet4Address> TEST_EXCL_SET =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ TEST_SERVER_ADDR, TEST_DEF_ROUTER, TEST_RESERVED_ADDR)));
+
+ @NonNull
+ private SharedLog mLog;
+ @NonNull @Mock
+ private Clock mClock;
+ @NonNull
+ private DhcpLeaseRepository mRepo;
+
+ private static Inet4Address parseAddr4(String inet4Addr) {
+ return (Inet4Address) parseNumericAddress(inet4Addr);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLog = new SharedLog("DhcpLeaseRepositoryTest");
+ when(mClock.elapsedRealtime()).thenReturn(TEST_TIME);
+ mRepo = new DhcpLeaseRepository(
+ TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock);
+ }
+
+ /**
+ * Request a number of addresses through offer/request. Useful to test address exhaustion.
+ * @param nAddr Number of addresses to request.
+ */
+ private void requestAddresses(byte nAddr) throws Exception {
+ final HashSet<Inet4Address> addrs = new HashSet<>();
+ byte[] hwAddrBytes = new byte[] { 8, 4, 3, 2, 1, 0 };
+ for (byte i = 0; i < nAddr; i++) {
+ hwAddrBytes[5] = i;
+ MacAddress newMac = MacAddress.fromBytes(hwAddrBytes);
+ final String hostname = "host_" + i;
+ final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname);
+
+ assertNotNull(lease);
+ assertEquals(newMac, lease.getHwAddr());
+ assertEquals(hostname, lease.getHostname());
+ assertTrue(format("Duplicate address allocated: %s in %s", lease.getNetAddr(), addrs),
+ addrs.add(lease.getNetAddr()));
+
+ requestLeaseSelecting(newMac, lease.getNetAddr(), hostname);
+ }
+ }
+
+ @Test
+ public void testAddressExhaustion() throws Exception {
+ // Use a /28 to quickly run out of addresses
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+
+ // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
+ requestAddresses((byte) 11);
+
+ try {
+ mRepo.getOffer(null, TEST_MAC_2,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ fail("Should be out of addresses");
+ } catch (DhcpLeaseRepository.OutOfAddressesException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testUpdateParams_LeaseCleanup() throws Exception {
+ // Inside /28:
+ final Inet4Address reqAddrIn28 = parseAddr4("192.168.42.242");
+ final Inet4Address declinedAddrIn28 = parseAddr4("192.168.42.245");
+
+ // Inside /28, but not available there (first address of the range)
+ final Inet4Address declinedFirstAddrIn28 = parseAddr4("192.168.42.240");
+
+ final DhcpLease reqAddrIn28Lease = requestLeaseSelecting(TEST_MAC_1, reqAddrIn28);
+ mRepo.markLeaseDeclined(declinedAddrIn28);
+ mRepo.markLeaseDeclined(declinedFirstAddrIn28);
+
+ // Inside /22, but outside /28:
+ final Inet4Address reqAddrIn22 = parseAddr4("192.168.42.3");
+ final Inet4Address declinedAddrIn22 = parseAddr4("192.168.42.4");
+
+ final DhcpLease reqAddrIn22Lease = requestLeaseSelecting(TEST_MAC_3, reqAddrIn22);
+ mRepo.markLeaseDeclined(declinedAddrIn22);
+
+ // Address that will be reserved in the updateParams call below
+ final Inet4Address reservedAddr = parseAddr4("192.168.42.244");
+ final DhcpLease reservedAddrLease = requestLeaseSelecting(TEST_MAC_2, reservedAddr);
+
+ // Update from /22 to /28 and add another reserved address
+ Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET);
+ newReserved.add(reservedAddr);
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS);
+
+ assertHasLease(reqAddrIn28Lease);
+ assertDeclined(declinedAddrIn28);
+
+ assertNotDeclined(declinedFirstAddrIn28);
+
+ assertNoLease(reqAddrIn22Lease);
+ assertNotDeclined(declinedAddrIn22);
+
+ assertNoLease(reservedAddrLease);
+ }
+
+ @Test
+ public void testGetOffer_StableAddress() throws Exception {
+ for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
+ final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+
+ // Same lease is offered twice
+ final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertEquals(lease, newLease);
+ }
+ }
+
+ @Test
+ public void testUpdateParams_UsesNewPrefix() throws Exception {
+ final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24);
+ mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+
+ DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertTrue(newPrefix.contains(lease.getNetAddr()));
+ }
+
+ @Test
+ public void testGetOffer_ExistingLease() throws Exception {
+ requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, TEST_HOSTNAME_1);
+
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertEquals(TEST_INETADDR_1, offer.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, offer.getHostname());
+ }
+
+ @Test
+ public void testGetOffer_ClientIdHasExistingLease() throws Exception {
+ final byte[] clientId = new byte[] { 1, 2 };
+ mRepo.requestLease(clientId, TEST_MAC_1, INET4_ANY /* clientAddr */,
+ INET4_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, TEST_HOSTNAME_1);
+
+ // Different MAC, but same clientId
+ DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertEquals(TEST_INETADDR_1, offer.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, offer.getHostname());
+ }
+
+ @Test
+ public void testGetOffer_DifferentClientId() throws Exception {
+ final byte[] clientId1 = new byte[] { 1, 2 };
+ final byte[] clientId2 = new byte[] { 3, 4 };
+ mRepo.requestLease(clientId1, TEST_MAC_1, INET4_ANY /* clientAddr */,
+ INET4_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, TEST_HOSTNAME_1);
+
+ // Same MAC, different client ID
+ DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ // Obtains a different address
+ assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
+ assertEquals(HOSTNAME_NONE, offer.getHostname());
+ assertEquals(TEST_MAC_1, offer.getHwAddr());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddress() throws Exception {
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
+ TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1);
+ assertEquals(TEST_INETADDR_1, offer.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, offer.getHostname());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddressInUse() throws Exception {
+ requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY /* relayAddr */,
+ TEST_INETADDR_1 /* reqAddr */, HOSTNAME_NONE);
+ assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddressReserved() throws Exception {
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
+ TEST_RESERVED_ADDR /* reqAddr */, HOSTNAME_NONE);
+ assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddressInvalid() throws Exception {
+ final Inet4Address invalidAddr = parseAddr4("192.168.42.0");
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
+ invalidAddr /* reqAddr */, HOSTNAME_NONE);
+ assertNotEquals(invalidAddr, offer.getNetAddr());
+ }
+
+ @Test
+ public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception {
+ final Inet4Address invalidAddr = parseAddr4("192.168.254.2");
+ DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
+ invalidAddr /* reqAddr */, HOSTNAME_NONE);
+ assertNotEquals(invalidAddr, offer.getNetAddr());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
+ public void testGetOffer_RelayInInvalidSubnet() throws Exception {
+ mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* relayAddr */,
+ INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ }
+
+ @Test
+ public void testRequestLease_SelectingTwice() throws Exception {
+ final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1,
+ TEST_HOSTNAME_1);
+
+ // Second request from same client for a different address
+ final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_2,
+ TEST_HOSTNAME_2);
+
+ assertEquals(TEST_INETADDR_1, lease1.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, lease1.getHostname());
+
+ assertEquals(TEST_INETADDR_2, lease2.getNetAddr());
+ assertEquals(TEST_HOSTNAME_2, lease2.getHostname());
+
+ // First address freed when client requested a different one: another client can request it
+ final DhcpLease lease3 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1, HOSTNAME_NONE);
+ assertEquals(TEST_INETADDR_1, lease3.getNetAddr());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_SelectingInvalid() throws Exception {
+ requestLeaseSelecting(TEST_MAC_1, parseAddr4("192.168.254.5"));
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_SelectingInUse() throws Exception {
+ requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
+ requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_SelectingReserved() throws Exception {
+ requestLeaseSelecting(TEST_MAC_1, TEST_RESERVED_ADDR);
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
+ public void testRequestLease_SelectingRelayInInvalidSubnet() throws Exception {
+ mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* clientAddr */,
+ parseAddr4("192.168.128.1") /* relayAddr */, TEST_INETADDR_1 /* reqAddr */,
+ true /* sidSet */, HOSTNAME_NONE);
+ }
+
+ @Test
+ public void testRequestLease_InitReboot() throws Exception {
+ // Request address once
+ requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
+
+ final long newTime = TEST_TIME + 100;
+ when(mClock.elapsedRealtime()).thenReturn(newTime);
+
+ // init-reboot (sidSet == false): verify configuration
+ final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_1);
+ assertEquals(TEST_INETADDR_1, lease.getNetAddr());
+ assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_InitRebootWrongAddr() throws Exception {
+ // Request address once
+ requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
+ // init-reboot with different requested address
+ requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
+ }
+
+ @Test
+ public void testRequestLease_InitRebootUnknownAddr() throws Exception {
+ // init-reboot with unknown requested address
+ final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
+ // RFC2131 says we should not reply to accommodate other servers, but since we are
+ // authoritative we allow creating the lease to avoid issues with lost lease DB (same as
+ // dnsmasq behavior)
+ assertEquals(TEST_INETADDR_2, lease.getNetAddr());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_InitRebootWrongSubnet() throws Exception {
+ requestLeaseInitReboot(TEST_MAC_1, parseAddr4("192.168.254.2"));
+ }
+
+ @Test
+ public void testRequestLease_Renewing() throws Exception {
+ requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
+
+ final long newTime = TEST_TIME + 100;
+ when(mClock.elapsedRealtime()).thenReturn(newTime);
+
+ final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
+
+ assertEquals(TEST_INETADDR_1, lease.getNetAddr());
+ assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
+ }
+
+ @Test
+ public void testRequestLease_RenewingUnknownAddr() throws Exception {
+ final long newTime = TEST_TIME + 100;
+ when(mClock.elapsedRealtime()).thenReturn(newTime);
+ final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
+ // Allows renewing an unknown address if available
+ assertEquals(TEST_INETADDR_1, lease.getNetAddr());
+ assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_RenewingAddrInUse() throws Exception {
+ requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
+ requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
+ }
+
+ @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
+ public void testRequestLease_RenewingInvalidAddr() throws Exception {
+ requestLeaseRenewing(TEST_MAC_1, parseAddr4("192.168.254.2"));
+ }
+
+ @Test
+ public void testReleaseLease() throws Exception {
+ final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
+
+ assertHasLease(lease1);
+ assertTrue(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
+ assertNoLease(lease1);
+
+ final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
+ assertEquals(TEST_INETADDR_1, lease2.getNetAddr());
+ }
+
+ @Test
+ public void testReleaseLease_UnknownLease() {
+ assertFalse(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
+ }
+
+ @Test
+ public void testReleaseLease_StableOffer() throws Exception {
+ for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
+ final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+
+ requestLeaseSelecting(mac, lease.getNetAddr());
+ mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr());
+
+ // Same lease is offered after it was released
+ final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertEquals(lease.getNetAddr(), newLease.getNetAddr());
+ }
+ }
+
+ @Test
+ public void testMarkLeaseDeclined() throws Exception {
+ final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+
+ mRepo.markLeaseDeclined(lease.getNetAddr());
+
+ // Same lease is not offered again
+ final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ assertNotEquals(lease.getNetAddr(), newLease.getNetAddr());
+ }
+
+ @Test
+ public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception {
+ // Use a /28 to quickly run out of addresses
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+
+ mRepo.markLeaseDeclined(TEST_INETADDR_1);
+ mRepo.markLeaseDeclined(TEST_INETADDR_2);
+
+ // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
+ requestAddresses((byte) 9);
+
+ // Last 2 addresses: addresses marked declined should be used
+ final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1);
+ requestLeaseSelecting(TEST_MAC_1, firstLease.getNetAddr());
+
+ final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2,
+ INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2);
+ requestLeaseSelecting(TEST_MAC_2, secondLease.getNetAddr());
+
+ // Now out of addresses
+ try {
+ mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, INET4_ANY /* relayAddr */,
+ INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ fail("Repository should be out of addresses and throw");
+ } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ }
+
+ assertEquals(TEST_INETADDR_1, firstLease.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, firstLease.getHostname());
+ assertEquals(TEST_INETADDR_2, secondLease.getNetAddr());
+ assertEquals(TEST_HOSTNAME_2, secondLease.getHostname());
+ }
+
+ private DhcpLease requestLease(@NonNull MacAddress macAddr, @NonNull Inet4Address clientAddr,
+ @Nullable Inet4Address reqAddr, @Nullable String hostname, boolean sidSet)
+ throws DhcpLeaseRepository.DhcpLeaseException {
+ return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr, INET4_ANY /* relayAddr */,
+ reqAddr, sidSet, hostname);
+ }
+
+ /**
+ * Request a lease simulating a client in the SELECTING state.
+ */
+ private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
+ @NonNull Inet4Address reqAddr, @Nullable String hostname)
+ throws DhcpLeaseRepository.DhcpLeaseException {
+ return requestLease(macAddr, INET4_ANY /* clientAddr */, reqAddr, hostname,
+ true /* sidSet */);
+ }
+
+ /**
+ * Request a lease simulating a client in the SELECTING state.
+ */
+ private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
+ @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
+ return requestLeaseSelecting(macAddr, reqAddr, HOSTNAME_NONE);
+ }
+
+ /**
+ * Request a lease simulating a client in the INIT-REBOOT state.
+ */
+ private DhcpLease requestLeaseInitReboot(@NonNull MacAddress macAddr,
+ @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
+ return requestLease(macAddr, INET4_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE,
+ false /* sidSet */);
+ }
+
+ /**
+ * Request a lease simulating a client in the RENEWING state.
+ */
+ private DhcpLease requestLeaseRenewing(@NonNull MacAddress macAddr,
+ @NonNull Inet4Address clientAddr) throws DhcpLeaseRepository.DhcpLeaseException {
+ // Renewing: clientAddr filled in, no reqAddr
+ return requestLease(macAddr, clientAddr, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE,
+ true /* sidSet */);
+ }
+
+ private void assertNoLease(DhcpLease lease) {
+ assertFalse("Leases contain " + lease, mRepo.getCommittedLeases().contains(lease));
+ }
+
+ private void assertHasLease(DhcpLease lease) {
+ assertTrue("Leases do not contain " + lease, mRepo.getCommittedLeases().contains(lease));
+ }
+
+ private void assertNotDeclined(Inet4Address addr) {
+ assertFalse("Address is declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
+ }
+
+ private void assertDeclined(Inet4Address addr) {
+ assertTrue("Address is not declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
+ }
+}