Address a TODO in BpfMapTest.

Test: test-only change
Change-Id: I9a47234979cbb161dfcd0c97c54c0476aa753c5e
This commit is contained in:
Lorenzo Colitti
2021-01-27 00:45:57 +09:00
parent 5b1ed508cf
commit 7bf39e56e1

View File

@@ -35,7 +35,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@@ -56,6 +55,8 @@ public final class BpfMapTest {
private ArrayMap<TetherDownstream6Key, Tether6Value> mTestData; private ArrayMap<TetherDownstream6Key, Tether6Value> mTestData;
private BpfMap<TetherDownstream6Key, Tether6Value> mTestMap;
@BeforeClass @BeforeClass
public static void setupOnce() { public static void setupOnce() {
System.loadLibrary("tetherutilsjni"); System.loadLibrary("tetherutilsjni");
@@ -63,12 +64,6 @@ public final class BpfMapTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
// TODO: Simply the test map creation and deletion.
// - Make the map a class member (mTestMap)
// - Open the test map RW in setUp
// - Close the test map in tearDown.
cleanTestMap();
mTestData = new ArrayMap<>(); mTestData = new ArrayMap<>();
mTestData.put(createTetherDownstream6Key(101, "2001:db8::1"), mTestData.put(createTetherDownstream6Key(101, "2001:db8::1"),
createTether6Value(11, "00:00:00:00:00:0a", "11:11:11:00:00:0b", createTether6Value(11, "00:00:00:00:00:0a", "11:11:11:00:00:0b",
@@ -79,30 +74,23 @@ public final class BpfMapTest {
mTestData.put(createTetherDownstream6Key(103, "2001:db8::3"), mTestData.put(createTetherDownstream6Key(103, "2001:db8::3"),
createTether6Value(33, "00:00:00:00:00:0e", "33:33:33:00:00:0f", createTether6Value(33, "00:00:00:00:00:0e", "33:33:33:00:00:0f",
ETH_P_IPV6, 1500)); ETH_P_IPV6, 1500));
initTestMap();
} }
@After private void initTestMap() throws Exception {
public void tearDown() throws Exception { mTestMap = new BpfMap<>(
cleanTestMap();
}
private BpfMap<TetherDownstream6Key, Tether6Value> getTestMap() throws Exception {
return new BpfMap<>(
TETHER_DOWNSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR, TETHER_DOWNSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
TetherDownstream6Key.class, Tether6Value.class); TetherDownstream6Key.class, Tether6Value.class);
}
private void cleanTestMap() throws Exception { mTestMap.forEach((key, value) -> {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { try {
bpfMap.forEach((key, value) -> { assertTrue(mTestMap.deleteEntry(key));
try { } catch (ErrnoException e) {
assertTrue(bpfMap.deleteEntry(key)); fail("Fail to delete the key " + key + ": " + e);
} catch (ErrnoException e) { }
fail("Fail to delete the key " + key + ": " + e); });
} assertNull(mTestMap.getFirstKey());
});
assertNull(bpfMap.getFirstKey());
}
} }
private TetherDownstream6Key createTetherDownstream6Key(long iif, String address) private TetherDownstream6Key createTetherDownstream6Key(long iif, String address)
@@ -112,8 +100,7 @@ public final class BpfMapTest {
return new TetherDownstream6Key(iif, ipv6Address.getAddress()); return new TetherDownstream6Key(iif, ipv6Address.getAddress());
} }
private Tether6Value createTether6Value(int oif, String src, String dst, private Tether6Value createTether6Value(int oif, String src, String dst, int proto, int pmtu) {
int proto, int pmtu) throws Exception {
final MacAddress srcMac = MacAddress.fromString(src); final MacAddress srcMac = MacAddress.fromString(src);
final MacAddress dstMac = MacAddress.fromString(dst); final MacAddress dstMac = MacAddress.fromString(dst);
@@ -150,210 +137,192 @@ public final class BpfMapTest {
@Test @Test
public void testGetFirstKey() throws Exception { public void testGetFirstKey() throws Exception {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { // getFirstKey on an empty map returns null.
// getFirstKey on an empty map returns null. assertFalse(mTestMap.containsKey(mTestData.keyAt(0)));
assertFalse(bpfMap.containsKey(mTestData.keyAt(0))); assertNull(mTestMap.getFirstKey());
assertNull(bpfMap.getFirstKey()); assertNull(mTestMap.getValue(mTestData.keyAt(0)));
assertNull(bpfMap.getValue(mTestData.keyAt(0)));
// getFirstKey on a non-empty map returns the first key. // getFirstKey on a non-empty map returns the first key.
bpfMap.insertEntry(mTestData.keyAt(0), mTestData.valueAt(0)); mTestMap.insertEntry(mTestData.keyAt(0), mTestData.valueAt(0));
assertEquals(mTestData.keyAt(0), bpfMap.getFirstKey()); assertEquals(mTestData.keyAt(0), mTestMap.getFirstKey());
}
} }
@Test @Test
public void testGetNextKey() throws Exception { public void testGetNextKey() throws Exception {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { // [1] If the passed-in key is not found on empty map, return null.
// [1] If the passed-in key is not found on empty map, return null. final TetherDownstream6Key nonexistentKey =
final TetherDownstream6Key nonexistentKey = createTetherDownstream6Key(1234, "2001:db8::10");
createTetherDownstream6Key(1234, "2001:db8::10"); assertNull(mTestMap.getNextKey(nonexistentKey));
assertNull(bpfMap.getNextKey(nonexistentKey));
// [2] If the passed-in key is null on empty map, throw NullPointerException. // [2] If the passed-in key is null on empty map, throw NullPointerException.
try { try {
bpfMap.getNextKey(null); mTestMap.getNextKey(null);
fail("Getting next key with null key should throw NullPointerException"); fail("Getting next key with null key should throw NullPointerException");
} catch (NullPointerException expected) { } } catch (NullPointerException expected) { }
// The BPF map has one entry now. // The BPF map has one entry now.
final ArrayMap<TetherDownstream6Key, Tether6Value> resultMap = final ArrayMap<TetherDownstream6Key, Tether6Value> resultMap =
new ArrayMap<>(); new ArrayMap<>();
bpfMap.insertEntry(mTestData.keyAt(0), mTestData.valueAt(0)); mTestMap.insertEntry(mTestData.keyAt(0), mTestData.valueAt(0));
resultMap.put(mTestData.keyAt(0), mTestData.valueAt(0)); resultMap.put(mTestData.keyAt(0), mTestData.valueAt(0));
// [3] If the passed-in key is the last key, return null. // [3] If the passed-in key is the last key, return null.
// Because there is only one entry in the map, the first key equals the last key. // Because there is only one entry in the map, the first key equals the last key.
final TetherDownstream6Key lastKey = bpfMap.getFirstKey(); final TetherDownstream6Key lastKey = mTestMap.getFirstKey();
assertNull(bpfMap.getNextKey(lastKey)); assertNull(mTestMap.getNextKey(lastKey));
// The BPF map has two entries now. // The BPF map has two entries now.
bpfMap.insertEntry(mTestData.keyAt(1), mTestData.valueAt(1)); mTestMap.insertEntry(mTestData.keyAt(1), mTestData.valueAt(1));
resultMap.put(mTestData.keyAt(1), mTestData.valueAt(1)); resultMap.put(mTestData.keyAt(1), mTestData.valueAt(1));
// [4] If the passed-in key is found, return the next key. // [4] If the passed-in key is found, return the next key.
TetherDownstream6Key nextKey = bpfMap.getFirstKey(); TetherDownstream6Key nextKey = mTestMap.getFirstKey();
while (nextKey != null) { while (nextKey != null) {
if (resultMap.remove(nextKey).equals(nextKey)) { if (resultMap.remove(nextKey).equals(nextKey)) {
fail("Unexpected result: " + nextKey); fail("Unexpected result: " + nextKey);
}
nextKey = bpfMap.getNextKey(nextKey);
} }
assertTrue(resultMap.isEmpty()); nextKey = mTestMap.getNextKey(nextKey);
// [5] If the passed-in key is not found on non-empty map, return the first key.
assertEquals(bpfMap.getFirstKey(), bpfMap.getNextKey(nonexistentKey));
// [6] If the passed-in key is null on non-empty map, throw NullPointerException.
try {
bpfMap.getNextKey(null);
fail("Getting next key with null key should throw NullPointerException");
} catch (NullPointerException expected) { }
} }
assertTrue(resultMap.isEmpty());
// [5] If the passed-in key is not found on non-empty map, return the first key.
assertEquals(mTestMap.getFirstKey(), mTestMap.getNextKey(nonexistentKey));
// [6] If the passed-in key is null on non-empty map, throw NullPointerException.
try {
mTestMap.getNextKey(null);
fail("Getting next key with null key should throw NullPointerException");
} catch (NullPointerException expected) { }
} }
@Test @Test
public void testUpdateBpfMap() throws Exception { public void testUpdateBpfMap() throws Exception {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { final TetherDownstream6Key key = mTestData.keyAt(0);
final Tether6Value value = mTestData.valueAt(0);
final Tether6Value value2 = mTestData.valueAt(1);
assertFalse(mTestMap.deleteEntry(key));
final TetherDownstream6Key key = mTestData.keyAt(0); // updateEntry will create an entry if it does not exist already.
final Tether6Value value = mTestData.valueAt(0); mTestMap.updateEntry(key, value);
final Tether6Value value2 = mTestData.valueAt(1); assertTrue(mTestMap.containsKey(key));
assertFalse(bpfMap.deleteEntry(key)); final Tether6Value result = mTestMap.getValue(key);
assertEquals(value, result);
// updateEntry will create an entry if it does not exist already. // updateEntry will update an entry that already exists.
bpfMap.updateEntry(key, value); mTestMap.updateEntry(key, value2);
assertTrue(bpfMap.containsKey(key)); assertTrue(mTestMap.containsKey(key));
final Tether6Value result = bpfMap.getValue(key); final Tether6Value result2 = mTestMap.getValue(key);
assertEquals(value, result); assertEquals(value2, result2);
// updateEntry will update an entry that already exists. assertTrue(mTestMap.deleteEntry(key));
bpfMap.updateEntry(key, value2); assertFalse(mTestMap.containsKey(key));
assertTrue(bpfMap.containsKey(key));
final Tether6Value result2 = bpfMap.getValue(key);
assertEquals(value2, result2);
assertTrue(bpfMap.deleteEntry(key));
assertFalse(bpfMap.containsKey(key));
}
} }
@Test @Test
public void testInsertReplaceEntry() throws Exception { public void testInsertReplaceEntry() throws Exception {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { final TetherDownstream6Key key = mTestData.keyAt(0);
final Tether6Value value = mTestData.valueAt(0);
final Tether6Value value2 = mTestData.valueAt(1);
final TetherDownstream6Key key = mTestData.keyAt(0); try {
final Tether6Value value = mTestData.valueAt(0); mTestMap.replaceEntry(key, value);
final Tether6Value value2 = mTestData.valueAt(1); fail("Replacing non-existent key " + key + " should throw NoSuchElementException");
} catch (NoSuchElementException expected) { }
assertFalse(mTestMap.containsKey(key));
try { mTestMap.insertEntry(key, value);
bpfMap.replaceEntry(key, value); assertTrue(mTestMap.containsKey(key));
fail("Replacing non-existent key " + key + " should throw NoSuchElementException"); final Tether6Value result = mTestMap.getValue(key);
} catch (NoSuchElementException expected) { } assertEquals(value, result);
assertFalse(bpfMap.containsKey(key)); try {
mTestMap.insertEntry(key, value);
fail("Inserting existing key " + key + " should throw IllegalStateException");
} catch (IllegalStateException expected) { }
bpfMap.insertEntry(key, value); mTestMap.replaceEntry(key, value2);
assertTrue(bpfMap.containsKey(key)); assertTrue(mTestMap.containsKey(key));
final Tether6Value result = bpfMap.getValue(key); final Tether6Value result2 = mTestMap.getValue(key);
assertEquals(value, result); assertEquals(value2, result2);
try {
bpfMap.insertEntry(key, value);
fail("Inserting existing key " + key + " should throw IllegalStateException");
} catch (IllegalStateException expected) { }
bpfMap.replaceEntry(key, value2);
assertTrue(bpfMap.containsKey(key));
final Tether6Value result2 = bpfMap.getValue(key);
assertEquals(value2, result2);
}
} }
@Test @Test
public void testIterateBpfMap() throws Exception { public void testIterateBpfMap() throws Exception {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { final ArrayMap<TetherDownstream6Key, Tether6Value> resultMap =
final ArrayMap<TetherDownstream6Key, Tether6Value> resultMap = new ArrayMap<>(mTestData);
new ArrayMap<>(mTestData);
for (int i = 0; i < resultMap.size(); i++) { for (int i = 0; i < resultMap.size(); i++) {
bpfMap.insertEntry(resultMap.keyAt(i), resultMap.valueAt(i)); mTestMap.insertEntry(resultMap.keyAt(i), resultMap.valueAt(i));
}
bpfMap.forEach((key, value) -> {
if (!value.equals(resultMap.remove(key))) {
fail("Unexpected result: " + key + ", value: " + value);
}
});
assertTrue(resultMap.isEmpty());
} }
mTestMap.forEach((key, value) -> {
if (!value.equals(resultMap.remove(key))) {
fail("Unexpected result: " + key + ", value: " + value);
}
});
assertTrue(resultMap.isEmpty());
} }
@Test @Test
public void testIterateEmptyMap() throws Exception { public void testIterateEmptyMap() throws Exception {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { // Can't use an int because variables used in a lambda must be final.
// Can't use an int because variables used in a lambda must be final. final AtomicInteger count = new AtomicInteger();
final AtomicInteger count = new AtomicInteger(); mTestMap.forEach((key, value) -> count.incrementAndGet());
bpfMap.forEach((key, value) -> count.incrementAndGet()); // Expect that the consumer was never called.
// Expect that the consumer was never called. assertEquals(0, count.get());
assertEquals(0, count.get());
}
} }
@Test @Test
public void testIterateDeletion() throws Exception { public void testIterateDeletion() throws Exception {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { final ArrayMap<TetherDownstream6Key, Tether6Value> resultMap =
final ArrayMap<TetherDownstream6Key, Tether6Value> resultMap = new ArrayMap<>(mTestData);
new ArrayMap<>(mTestData);
for (int i = 0; i < resultMap.size(); i++) { for (int i = 0; i < resultMap.size(); i++) {
bpfMap.insertEntry(resultMap.keyAt(i), resultMap.valueAt(i)); mTestMap.insertEntry(resultMap.keyAt(i), resultMap.valueAt(i));
}
// Can't use an int because variables used in a lambda must be final.
final AtomicInteger count = new AtomicInteger();
bpfMap.forEach((key, value) -> {
try {
assertTrue(bpfMap.deleteEntry(key));
} catch (ErrnoException e) {
fail("Fail to delete key " + key + ": " + e);
}
if (!value.equals(resultMap.remove(key))) {
fail("Unexpected result: " + key + ", value: " + value);
}
count.incrementAndGet();
});
assertEquals(3, count.get());
assertTrue(resultMap.isEmpty());
assertNull(bpfMap.getFirstKey());
} }
// Can't use an int because variables used in a lambda must be final.
final AtomicInteger count = new AtomicInteger();
mTestMap.forEach((key, value) -> {
try {
assertTrue(mTestMap.deleteEntry(key));
} catch (ErrnoException e) {
fail("Fail to delete key " + key + ": " + e);
}
if (!value.equals(resultMap.remove(key))) {
fail("Unexpected result: " + key + ", value: " + value);
}
count.incrementAndGet();
});
assertEquals(3, count.get());
assertTrue(resultMap.isEmpty());
assertNull(mTestMap.getFirstKey());
} }
@Test @Test
public void testInsertOverflow() throws Exception { public void testInsertOverflow() throws Exception {
try (BpfMap<TetherDownstream6Key, Tether6Value> bpfMap = getTestMap()) { final ArrayMap<TetherDownstream6Key, Tether6Value> testData =
final ArrayMap<TetherDownstream6Key, Tether6Value> testData = new ArrayMap<>();
new ArrayMap<>();
// Build test data for TEST_MAP_SIZE + 1 entries. // Build test data for TEST_MAP_SIZE + 1 entries.
for (int i = 1; i <= TEST_MAP_SIZE + 1; i++) { for (int i = 1; i <= TEST_MAP_SIZE + 1; i++) {
testData.put(createTetherDownstream6Key(i, "2001:db8::1"), testData.put(createTetherDownstream6Key(i, "2001:db8::1"),
createTether6Value(100, "de:ad:be:ef:00:01", "de:ad:be:ef:00:02", createTether6Value(100, "de:ad:be:ef:00:01", "de:ad:be:ef:00:02",
ETH_P_IPV6, 1500)); ETH_P_IPV6, 1500));
} }
// Insert #TEST_MAP_SIZE test entries to the map. The map has reached the limit. // Insert #TEST_MAP_SIZE test entries to the map. The map has reached the limit.
for (int i = 0; i < TEST_MAP_SIZE; i++) { for (int i = 0; i < TEST_MAP_SIZE; i++) {
bpfMap.insertEntry(testData.keyAt(i), testData.valueAt(i)); mTestMap.insertEntry(testData.keyAt(i), testData.valueAt(i));
} }
// The map won't allow inserting any more entries. // The map won't allow inserting any more entries.
try { try {
bpfMap.insertEntry(testData.keyAt(TEST_MAP_SIZE), testData.valueAt(TEST_MAP_SIZE)); mTestMap.insertEntry(testData.keyAt(TEST_MAP_SIZE), testData.valueAt(TEST_MAP_SIZE));
fail("Writing too many entries should throw ErrnoException"); fail("Writing too many entries should throw ErrnoException");
} catch (ErrnoException expected) { } catch (ErrnoException expected) {
// Expect that can't insert the entry anymore because the number of elements in the // Expect that can't insert the entry anymore because the number of elements in the
// map reached the limit. See man-pages/bpf. // map reached the limit. See man-pages/bpf.
assertEquals(OsConstants.E2BIG, expected.errno); assertEquals(OsConstants.E2BIG, expected.errno);
}
} }
} }
} }